diff --git a/README.md b/README.md index 2065fcf0..4df2ed84 100644 --- a/README.md +++ b/README.md @@ -63,17 +63,21 @@ You need the following permissions to run this module. | Name | Source | Version | |------|--------|---------| +| [backup\_key\_crn\_parser](#module\_backup\_key\_crn\_parser) | terraform-ibm-modules/common-utilities/ibm//modules/crn-parser | 1.1.0 | | [cbr\_rule](#module\_cbr\_rule) | terraform-ibm-modules/cbr/ibm//modules/cbr-rule-module | 1.29.0 | +| [kms\_key\_crn\_parser](#module\_kms\_key\_crn\_parser) | terraform-ibm-modules/common-utilities/ibm//modules/crn-parser | 1.1.0 | ### Resources | Name | Type | |------|------| | [ibm_database.mongodb](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/database) | resource | +| [ibm_iam_authorization_policy.backup_kms_policy](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/iam_authorization_policy) | resource | | [ibm_iam_authorization_policy.kms_policy](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/iam_authorization_policy) | resource | | [ibm_resource_key.service_credentials](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/resource_key) | resource | | [ibm_resource_tag.mongodb_tag](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/resource_tag) | resource | | [time_sleep.wait_for_authorization_policy](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource | +| [time_sleep.wait_for_backup_kms_authorization_policy](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource | | [ibm_database_connection.database_connection](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/data-sources/database_connection) | data source | ### Inputs @@ -84,15 +88,13 @@ You need the following permissions to run this module. | [admin\_pass](#input\_admin\_pass) | The password for the database administrator. If the admin password is null then the admin user ID cannot be accessed. More users can be specified in a user block. | `string` | `null` | no | | [auto\_scaling](#input\_auto\_scaling) | Optional rules to allow the database to increase resources in response to usage. Only a single autoscaling block is allowed. Make sure you understand the effects of autoscaling, especially for production environments. See https://cloud.ibm.com/docs/databases-for-mongodb?topic=databases-for-mongodb-autoscaling&interface=cli#autoscaling-considerations in the IBM Cloud Docs. |
object({
disk = object({
capacity_enabled = optional(bool, false)
free_space_less_than_percent = optional(number, 10)
io_above_percent = optional(number, 90)
io_enabled = optional(bool, false)
io_over_period = optional(string, "15m")
rate_increase_percent = optional(number, 10)
rate_limit_mb_per_member = optional(number, 3670016)
rate_period_seconds = optional(number, 900)
rate_units = optional(string, "mb")
})
memory = object({
io_above_percent = optional(number, 90)
io_enabled = optional(bool, false)
io_over_period = optional(string, "15m")
rate_increase_percent = optional(number, 10)
rate_limit_mb_per_member = optional(number, 114688)
rate_period_seconds = optional(number, 900)
rate_units = optional(string, "mb")
})
})
| `null` | no | | [backup\_crn](#input\_backup\_crn) | The CRN of a backup resource to restore from. The backup is created by a database deployment with the same service ID. The backup is loaded after provisioning and the new deployment starts up that uses that data. A backup CRN is in the format crn:v1:<…>:backup:. If omitted, the database is provisioned empty. | `string` | `null` | no | -| [backup\_encryption\_key\_crn](#input\_backup\_encryption\_key\_crn) | The CRN of a KMS (Key Protect or Hyper Protect Crypto Services) key to use for encrypting the disk that holds deployment backups. Only used if var.kms\_encryption\_enabled is set to true. There are limitation per region on the type of KMS service (Key Protect or Hyper Protect Crypto Services) and region for those services. See https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok and https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups | `string` | `null` | no | +| [backup\_encryption\_key\_crn](#input\_backup\_encryption\_key\_crn) | The CRN of a Key Protect or Hyper Protect Crypto Services encryption key that you want to use for encrypting the disk that holds deployment backups. Applies only if `use_ibm_owned_encryption_key` is false and `use_same_kms_key_for_backups` is false. If no value is passed, and `use_same_kms_key_for_backups` is true, the value of `kms_key_crn` is used. Alternatively set `use_default_backup_encryption_key` to true to use the IBM Cloud Databases default encryption. Bare in mind that backups encryption is only available in certain regions. See [Bring your own key for backups](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok) and [Using the HPCS Key for Backup encryption](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups). | `string` | `null` | no | | [cbr\_rules](#input\_cbr\_rules) | (Optional, list) List of CBR rules to create |
list(object({
description = string
account_id = string
rule_contexts = list(object({
attributes = optional(list(object({
name = string
value = string
}))) }))
enforcement_mode = string
}))
| `[]` | no | | [cpu\_count](#input\_cpu\_count) | Allocated dedicated CPU per member. For shared CPU, set to 0. [Learn more](https://cloud.ibm.com/docs/databases-for-mongodb?topic=databases-for-mongodb-pricing#mongodb-scale-member) | `number` | `0` | no | | [disk\_mb](#input\_disk\_mb) | Allocated disk per member. [Learn more](https://cloud.ibm.com/docs/databases-for-mongodb?topic=databases-for-mongodb-pricing#mongodb-scale-member) | `number` | `10240` | no | | [endpoints](#input\_endpoints) | Specify whether you want to enable the public, private, or both service endpoints. Supported values are 'public', 'private', or 'public-and-private'. | `string` | `"private"` | no | -| [existing\_kms\_instance\_guid](#input\_existing\_kms\_instance\_guid) | The GUID of the Hyper Protect Crypto Services or Key Protect instance in which the key specified in var.kms\_key\_crn and var.backup\_encryption\_key\_crn is coming from. Required only if var.kms\_encryption\_enabled is set to true, var.skip\_iam\_authorization\_policy is set to false, and you pass a value for var.kms\_key\_crn, var.backup\_encryption\_key\_crn, or both. | `string` | `null` | no | | [instance\_name](#input\_instance\_name) | The name to give the MongoDB instance. | `string` | n/a | yes | -| [kms\_encryption\_enabled](#input\_kms\_encryption\_enabled) | Set this to true to control the encryption keys used to encrypt the data that you store in IBM Cloud® Databases. If set to false, the data is encrypted by using randomly generated keys. For more info on Key Protect integration, see https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect. For more info on HPCS integration, see https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs | `bool` | `false` | no | -| [kms\_key\_crn](#input\_kms\_key\_crn) | The root key CRN of a Key Management Services like Key Protect or Hyper Protect Crypto Services (HPCS) that you want to use for disk encryption. Only used if var.kms\_encryption\_enabled is set to true. | `string` | `null` | no | +| [kms\_key\_crn](#input\_kms\_key\_crn) | The CRN of a Key Protect or Hyper Protect Crypto Services encryption key to encrypt your data. Applies only if `use_ibm_owned_encryption_key` is false. By default this key is used for both deployment data and backups, but this behaviour can be altered using the `use_same_kms_key_for_backups` and `backup_encryption_key_crn` inputs. Bare in mind that backups encryption is only available in certain regions. See [Bring your own key for backups](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok) and [Using the HPCS Key for Backup encryption](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups). | `string` | `null` | no | | [member\_host\_flavor](#input\_member\_host\_flavor) | Allocated host flavor per member. [Learn more](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/database#host_flavor). | `string` | `null` | no | | [members](#input\_members) | Allocated number of members | `number` | `3` | no | | [memory\_mb](#input\_memory\_mb) | Allocated memory per member. [Learn more](https://cloud.ibm.com/docs/databases-for-mongodb?topic=databases-for-mongodb-pricing#mongodb-scale-member) | `number` | `4096` | no | @@ -101,9 +103,11 @@ You need the following permissions to run this module. | [region](#input\_region) | The region where you want to deploy your instance. | `string` | `"us-south"` | no | | [resource\_group\_id](#input\_resource\_group\_id) | The resource group ID where the MongoDB instance will be created. | `string` | n/a | yes | | [service\_credential\_names](#input\_service\_credential\_names) | Map of name, role for service credentials that you want to create for the database | `map(string)` | `{}` | no | -| [skip\_iam\_authorization\_policy](#input\_skip\_iam\_authorization\_policy) | Set to true to skip the creation of an IAM authorization policy that permits all MongoDB database instances in the resource group to read the encryption key from the KMS instance. If set to false, pass in a value for the KMS instance in the existing\_kms\_instance\_guid variable. In addition, no policy is created if var.kms\_encryption\_enabled is set to false. | `bool` | `false` | no | +| [skip\_iam\_authorization\_policy](#input\_skip\_iam\_authorization\_policy) | Set to true to skip the creation of IAM authorization policies that permits all Databases for MongoDB instances in the given resource group 'Reader' access to the Key Protect or Hyper Protect Crypto Services key that was provided in the `kms_key_crn` and `backup_encryption_key_crn` inputs. This policy is required in order to enable KMS encryption, so only skip creation if there is one already present in your account. No policy is created if `use_ibm_owned_encryption_key` is true. | `bool` | `false` | no | | [tags](#input\_tags) | Optional list of tags to be added to the MongoDB instance. | `list(any)` | `[]` | no | -| [use\_default\_backup\_encryption\_key](#input\_use\_default\_backup\_encryption\_key) | Set to true to use default ICD randomly generated keys. | `bool` | `false` | no | +| [use\_default\_backup\_encryption\_key](#input\_use\_default\_backup\_encryption\_key) | When `use_ibm_owned_encryption_key` is set to false, backups will be encrypted with either the key specified in `kms_key_crn`, or in `backup_encryption_key_crn` if a value is passed. If you do not want to use your own key for backups encryption, you can set this to `true` to use the IBM Cloud Databases default encryption for backups. Alternatively set `use_ibm_owned_encryption_key` to true to use the default encryption for both backups and deployment data. | `bool` | `false` | no | +| [use\_ibm\_owned\_encryption\_key](#input\_use\_ibm\_owned\_encryption\_key) | IBM Cloud Databases will secure your deployment's data at rest automatically with an encryption key that IBM hold. Alternatively, you may select your own Key Management System instance and encryption key (Key Protect or Hyper Protect Crypto Services) by setting this to false. If setting to false, a value must be passed for the `kms_key_crn` input. | `bool` | `true` | no | +| [use\_same\_kms\_key\_for\_backups](#input\_use\_same\_kms\_key\_for\_backups) | Set this to false if you wan't to use a different key that you own to encrypt backups. When set to false, a value is required for the `backup_encryption_key_crn` input. Alternatiely set `use_default_backup_encryption_key` to true to use the IBM Cloud Databases default encryption. Applies only if `use_ibm_owned_encryption_key` is false. Bare in mind that backups encryption is only available in certain regions. See [Bring your own key for backups](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok) and [Using the HPCS Key for Backup encryption](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups). | `bool` | `true` | no | | [users](#input\_users) | A list of users that you want to create on the database. Multiple blocks are allowed. The user password must be in the range of 10-32 characters. Be warned that in most case using IAM service credentials (via the var.service\_credential\_names) is sufficient to control access to the MongoDB instance. This blocks creates native MongoDB database users, more info on that can be found here https://cloud.ibm.com/docs/databases-for-mongodb?topic=databases-for-mongodb-user-management&interface=ui |
list(object({
name = string
password = string # pragma: allowlist secret
type = optional(string)
role = optional(string)
}))
| `[]` | no | ### Outputs diff --git a/common-dev-assets b/common-dev-assets index 8a13c7bc..66d1c66f 160000 --- a/common-dev-assets +++ b/common-dev-assets @@ -1 +1 @@ -Subproject commit 8a13c7bc97e6e00b17615320a48fb89fe2a099f7 +Subproject commit 66d1c66f5e189696eb8769f8f10a8cc80816172f diff --git a/cra-config.yaml b/cra-config.yaml index 6aeb714e..fbae9039 100644 --- a/cra-config.yaml +++ b/cra-config.yaml @@ -1,10 +1,12 @@ # More info about this file at https://github.com/terraform-ibm-modules/common-pipeline-assets/blob/main/.github/workflows/terraform-test-pipeline.md#cra-config-yaml version: "v1" CRA_TARGETS: - - CRA_TARGET: "examples/fscloud" + - CRA_TARGET: "solutions/standard" 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). CRA_ENVIRONMENT_VARIABLES: - TF_VAR_existing_at_instance_crn: "crn:v1:bluemix:public:logdnaat:eu-de:a/abac0df06b644a9cabc6e44f55b3880e:b1ef3365-dfbf-4d8f-8ac8-75f4f84d6f4a::" - TF_VAR_existing_kms_instance_guid: "e6dce284-e80f-46e1-a3c1-830f7adff7a9" - TF_VAR_kms_key_crn: "crn:v1:bluemix:public:hs-crypto:us-south:a/abac0df06b644a9cabc6e44f55b3880e:e6dce284-e80f-46e1-a3c1-830f7adff7a9:key:76170fae-4e0c-48c3-8ebe-326059ebb533" + TF_VAR_existing_kms_instance_crn: "crn:v1:bluemix:public:hs-crypto:us-south:a/abac0df06b644a9cabc6e44f55b3880e:e6dce284-e80f-46e1-a3c1-830f7adff7a9::" + TF_VAR_existing_kms_key_crn: "crn:v1:bluemix:public:hs-crypto:us-south:a/abac0df06b644a9cabc6e44f55b3880e:e6dce284-e80f-46e1-a3c1-830f7adff7a9:key:1368d2eb-3ed0-4a8b-b09c-2155895f01ea" + TF_VAR_use_existing_resource_group: true + TF_VAR_resource_group_name: "geretain-test-redis" + TF_VAR_provider_visibility: "public" diff --git a/examples/complete/main.tf b/examples/complete/main.tf index 8db56282..85a7be30 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -31,6 +31,11 @@ resource "ibm_is_subnet" "testacc_subnet" { # Key Protect All Inclusive ############################################################################## +locals { + data_key_name = "${var.prefix}-mongo" + backups_key_name = "${var.prefix}-mongo-backups" +} + module "key_protect_all_inclusive" { source = "terraform-ibm-modules/kms-all-inclusive/ibm" version = "4.19.2" @@ -45,7 +50,11 @@ module "key_protect_all_inclusive" { key_ring_name = "icd" keys = [ { - key_name = "${var.prefix}-mongodb" + key_name = local.data_key_name + force_delete = true + }, + { + key_name = local.backups_key_name force_delete = true } ] @@ -80,20 +89,27 @@ module "cbr_zone" { ############################################################################## module "mongodb" { - source = "../.." - resource_group_id = module.resource_group.resource_group_id - mongodb_version = var.mongodb_version - instance_name = "${var.prefix}-mongodb" - kms_encryption_enabled = true - admin_pass = var.admin_pass - users = var.users - existing_kms_instance_guid = module.key_protect_all_inclusive.kms_guid - region = var.region - kms_key_crn = module.key_protect_all_inclusive.keys["icd.${var.prefix}-mongodb"].crn - access_tags = var.access_tags - tags = var.resource_tags - service_credential_names = var.service_credential_names - member_host_flavor = "multitenant" + source = "../.." + resource_group_id = module.resource_group.resource_group_id + mongodb_version = var.mongodb_version + instance_name = "${var.prefix}-mongodb" + admin_pass = var.admin_pass + users = var.users + region = var.region + access_tags = var.access_tags + tags = var.resource_tags + # Example of how to use different KMS keys for data and backups + use_ibm_owned_encryption_key = false + use_same_kms_key_for_backups = false + kms_key_crn = module.key_protect_all_inclusive.keys["icd.${local.data_key_name}"].crn + backup_encryption_key_crn = module.key_protect_all_inclusive.keys["icd.${local.data_key_name}"].crn + service_credential_names = { + "mongodb_admin" : "Administrator", + "mongodb_operator" : "Operator", + "mongodb_viewer" : "Viewer", + "mongodb_editor" : "Editor", + } + member_host_flavor = "multitenant" cbr_rules = [ { description = "${var.prefix}-mongodb access only from vpc" diff --git a/examples/complete/variables.tf b/examples/complete/variables.tf index 79430546..6171e76e 100644 --- a/examples/complete/variables.tf +++ b/examples/complete/variables.tf @@ -40,17 +40,6 @@ variable "mongodb_version" { default = null } -variable "service_credential_names" { - description = "Map of name, role for service credentials that you want to create for the database" - type = map(string) - default = { - "mongodb_admin" : "Administrator", - "mongodb_operator" : "Operator", - "mongodb_viewer" : "Viewer", - "mongodb_editor" : "Editor", - } -} - variable "admin_pass" { type = string default = null diff --git a/examples/fscloud/main.tf b/examples/fscloud/main.tf index b8166265..d1e6814e 100644 --- a/examples/fscloud/main.tf +++ b/examples/fscloud/main.tf @@ -54,20 +54,32 @@ module "cbr_zone" { ############################################################################## module "mongodb" { - source = "../../modules/fscloud" - resource_group_id = module.resource_group.resource_group_id - instance_name = "${var.prefix}-mongodb" - region = var.region - tags = var.resource_tags - access_tags = var.access_tags - kms_key_crn = var.kms_key_crn - existing_kms_instance_guid = var.existing_kms_instance_guid - mongodb_version = var.mongodb_version - service_credential_names = var.service_credential_names - auto_scaling = var.auto_scaling - member_host_flavor = "b3c.4x16.encrypted" - backup_encryption_key_crn = var.backup_encryption_key_crn - backup_crn = var.backup_crn + source = "../../modules/fscloud" + resource_group_id = module.resource_group.resource_group_id + instance_name = "${var.prefix}-mongodb" + region = var.region + tags = var.resource_tags + access_tags = var.access_tags + kms_key_crn = var.kms_key_crn + backup_encryption_key_crn = var.backup_encryption_key_crn + backup_crn = var.backup_crn + mongodb_version = var.mongodb_version + service_credential_names = { + "mongodb_admin" : "Administrator", + "mongodb_operator" : "Operator", + "mongodb_viewer" : "Viewer", + "mongodb_editor" : "Editor", + } + auto_scaling = { + disk = { + capacity_enabled : true, + io_enabled : true + } + memory = { + io_enabled : true, + } + } + member_host_flavor = "b3c.4x16.encrypted" cbr_rules = [ { description = "${var.prefix}-mongodb access only from vpc" diff --git a/examples/fscloud/variables.tf b/examples/fscloud/variables.tf index 9a195046..61edf7d9 100644 --- a/examples/fscloud/variables.tf +++ b/examples/fscloud/variables.tf @@ -34,11 +34,6 @@ variable "access_tags" { default = [] } -variable "existing_kms_instance_guid" { - description = "The GUID of the Hyper Protect Crypto services in which the key specified in var.kms_key_crn is coming from" - type = string -} - variable "kms_key_crn" { type = string description = "The root key CRN of a Hyper Protect Crypto Services (HPCS) that you want to use for disk encryption. See https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs&interface=ui for more information on integrating HPCS with MongoDB instance." @@ -50,52 +45,6 @@ variable "mongodb_version" { default = null } -variable "service_credential_names" { - description = "Map of name, role for service credentials that you want to create for the database" - type = map(string) - default = { - "mongodb_admin" : "Administrator", - "mongodb_operator" : "Operator", - "mongodb_viewer" : "Viewer", - "mongodb_editor" : "Editor", - } -} - -variable "auto_scaling" { - type = object({ - disk = object({ - capacity_enabled = optional(bool) - free_space_less_than_percent = optional(number) - io_above_percent = optional(number) - io_enabled = optional(bool) - io_over_period = optional(string) - rate_increase_percent = optional(number) - rate_limit_mb_per_member = optional(number) - rate_period_seconds = optional(number) - rate_units = optional(string) - }) - memory = object({ - io_above_percent = optional(number) - io_enabled = optional(bool) - io_over_period = optional(string) - rate_increase_percent = optional(number) - rate_limit_mb_per_member = optional(number) - rate_period_seconds = optional(number) - rate_units = optional(string) - }) - }) - description = "Optional rules to allow the database to increase resources in response to usage. Only a single autoscaling block is allowed. Make sure you understand the effects of autoscaling, especially for production environments. See https://cloud.ibm.com/docs/databases-for-mongodb?topic=databases-for-mongodb-autoscaling&interface=cli#autoscaling-considerations in the IBM Cloud Docs." - default = { - disk = { - capacity_enabled : true, - io_enabled : true - } - memory = { - io_enabled : true, - } - } -} - variable "backup_crn" { type = string description = "The CRN of a backup resource to restore from. The backup is created by a database deployment with the same service ID. The backup is loaded after provisioning and the new deployment starts up that uses that data. A backup CRN is in the format crn:v1:<…>:backup:. If omitted, the database is provisioned empty." diff --git a/main.tf b/main.tf index 20a14116..df6c6e41 100644 --- a/main.tf +++ b/main.tf @@ -5,52 +5,164 @@ locals { # Validation (approach based on https://github.com/hashicorp/terraform/issues/25609#issuecomment-1057614400) # tflint-ignore: terraform_unused_declarations - validate_kms_values = !var.kms_encryption_enabled && (var.kms_key_crn != null || var.backup_encryption_key_crn != null) ? tobool("When passing values for var.backup_encryption_key_crn or var.kms_key_crn, you must set var.kms_encryption_enabled to true. Otherwise unset them to use default encryption") : true + validate_kms_values = var.use_ibm_owned_encryption_key && (var.kms_key_crn != null || var.backup_encryption_key_crn != null) ? tobool("When passing values for 'kms_key_crn' or 'backup_encryption_key_crn', you must set 'use_ibm_owned_encryption_key' to false. Otherwise unset them to use default encryption.") : true # tflint-ignore: terraform_unused_declarations - validate_kms_vars = var.kms_encryption_enabled && var.kms_key_crn == null && var.backup_encryption_key_crn == null ? tobool("When setting var.kms_encryption_enabled to true, a value must be passed for var.kms_key_crn and/or var.backup_encryption_key_crn") : true + validate_kms_vars = !var.use_ibm_owned_encryption_key && var.kms_key_crn == null ? tobool("When setting 'use_ibm_owned_encryption_key' to false, a value must be passed for 'kms_key_crn'.") : true # tflint-ignore: terraform_unused_declarations - validate_auth_policy = var.kms_encryption_enabled && var.skip_iam_authorization_policy == false && var.existing_kms_instance_guid == null ? tobool("When var.skip_iam_authorization_policy is set to false, and var.kms_encryption_enabled to true, a value must be passed for var.existing_kms_instance_guid in order to create the auth policy.") : true + validate_backup_key = !var.use_ibm_owned_encryption_key && var.backup_encryption_key_crn != null && (var.use_default_backup_encryption_key || var.use_same_kms_key_for_backups) ? tobool("When passing a value for 'backup_encryption_key_crn' you cannot set 'use_default_backup_encryption_key' to true or 'use_ibm_owned_encryption_key' to false.") : true # tflint-ignore: terraform_unused_declarations - validate_backup_key = var.backup_encryption_key_crn != null && var.use_default_backup_encryption_key == true ? tobool("When passing a value for 'backup_encryption_key_crn' you cannot set 'use_default_backup_encryption_key' to 'true'") : true + validate_backup_key_2 = !var.use_ibm_owned_encryption_key && var.backup_encryption_key_crn == null && !var.use_same_kms_key_for_backups ? tobool("When 'use_same_kms_key_for_backups' is set to false, a value needs to be passed for 'backup_encryption_key_crn'.") : true # If no value passed for 'backup_encryption_key_crn' use the value of 'kms_key_crn' and perform validation of 'kms_key_crn' to check if region is supported by backup encryption key. - # For more info, see https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok and https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups" - - backup_encryption_key_crn = var.use_default_backup_encryption_key == true ? null : (var.backup_encryption_key_crn != null ? var.backup_encryption_key_crn : var.kms_key_crn) + # If 'use_ibm_owned_encryption_key' is true or 'use_default_backup_encryption_key' is true, default to null. + # If no value is passed for 'backup_encryption_key_crn', then default to use 'kms_key_crn'. + backup_encryption_key_crn = var.use_ibm_owned_encryption_key || var.use_default_backup_encryption_key ? null : (var.backup_encryption_key_crn != null ? var.backup_encryption_key_crn : var.kms_key_crn) # Determine if auto scaling is enabled auto_scaling_enabled = var.auto_scaling == null ? [] : [1] # Determine if host_flavor is used host_flavor_set = var.member_host_flavor != null ? true : false +} + +######################################################################################################################## +# Parse info from KMS key CRNs +######################################################################################################################## + +module "kms_key_crn_parser" { + count = var.use_ibm_owned_encryption_key ? 0 : 1 + source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser" + version = "1.1.0" + crn = var.kms_key_crn +} - # Determine what KMS service is being used for database encryption - kms_service = var.kms_key_crn != null ? ( - can(regex(".*kms.*", var.kms_key_crn)) ? "kms" : ( - can(regex(".*hs-crypto.*", var.kms_key_crn)) ? "hs-crypto" : "unrecognized key type" - ) - ) : "no key crn" +module "backup_key_crn_parser" { + count = var.use_ibm_owned_encryption_key ? 0 : 1 + source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser" + version = "1.1.0" + crn = local.backup_encryption_key_crn +} + +# Put parsed values into locals +locals { + kms_service = !var.use_ibm_owned_encryption_key ? module.kms_key_crn_parser[0].service_name : null + kms_account_id = !var.use_ibm_owned_encryption_key ? module.kms_key_crn_parser[0].account_id : null + kms_key_id = !var.use_ibm_owned_encryption_key ? module.kms_key_crn_parser[0].resource : null + kms_key_instance_guid = !var.use_ibm_owned_encryption_key ? module.kms_key_crn_parser[0].service_instance : null + backup_kms_service = !var.use_ibm_owned_encryption_key ? module.backup_key_crn_parser[0].service_name : null + backup_kms_account_id = !var.use_ibm_owned_encryption_key ? module.backup_key_crn_parser[0].account_id : null + backup_kms_key_id = !var.use_ibm_owned_encryption_key ? module.backup_key_crn_parser[0].resource : null + backup_kms_key_instance_guid = !var.use_ibm_owned_encryption_key ? module.backup_key_crn_parser[0].service_instance : null +} + +######################################################################################################################## +# KMS IAM Authorization Policies +######################################################################################################################## + +locals { + # only create auth policy if 'use_ibm_owned_encryption_key' is false, and 'skip_iam_authorization_policy' is false + create_kms_auth_policy = !var.use_ibm_owned_encryption_key && !var.skip_iam_authorization_policy ? 1 : 0 + # only create backup auth policy if 'use_ibm_owned_encryption_key' is false, 'skip_iam_authorization_policy' is false and 'use_same_kms_key_for_backups' is false + create_backup_kms_auth_policy = !var.use_ibm_owned_encryption_key && !var.skip_iam_authorization_policy && !var.use_same_kms_key_for_backups ? 1 : 0 } # Create IAM Authorization Policies to allow MongoDB to access KMS for the encryption key resource "ibm_iam_authorization_policy" "kms_policy" { - count = var.kms_encryption_enabled == false || var.skip_iam_authorization_policy ? 0 : 1 - source_service_name = "databases-for-mongodb" - source_resource_group_id = var.resource_group_id - target_service_name = local.kms_service - target_resource_instance_id = var.existing_kms_instance_guid - roles = ["Reader"] - description = "Allow all MongoDB instances in the resource group ${var.resource_group_id} to read from the ${local.kms_service} instance GUID ${var.existing_kms_instance_guid}" + count = local.create_kms_auth_policy + source_service_name = "databases-for-mongodb" + source_resource_group_id = var.resource_group_id + roles = ["Reader"] + description = "Allow all MongoDB instances in the resource group ${var.resource_group_id} to read the ${local.kms_service} key ${local.kms_key_id} from the instance GUID ${local.kms_key_instance_guid}" + resource_attributes { + name = "serviceName" + operator = "stringEquals" + value = local.kms_service + } + resource_attributes { + name = "accountId" + operator = "stringEquals" + value = local.kms_account_id + } + resource_attributes { + name = "serviceInstance" + operator = "stringEquals" + value = local.kms_key_instance_guid + } + resource_attributes { + name = "resourceType" + operator = "stringEquals" + value = "key" + } + resource_attributes { + name = "resource" + operator = "stringEquals" + value = local.kms_key_id + } + # Scope of policy now includes the key, so ensure to create new policy before + # destroying old one to prevent any disruption to every day services. + lifecycle { + create_before_destroy = true + } } # workaround for https://github.com/IBM-Cloud/terraform-provider-ibm/issues/4478 resource "time_sleep" "wait_for_authorization_policy" { + count = local.create_kms_auth_policy depends_on = [ibm_iam_authorization_policy.kms_policy] create_duration = "30s" } +resource "ibm_iam_authorization_policy" "backup_kms_policy" { + count = local.create_backup_kms_auth_policy + source_service_name = "databases-for-mongodb" + source_resource_group_id = var.resource_group_id + roles = ["Reader"] + description = "Allow all MongoDB instances in the Resource Group ${var.resource_group_id} to read the ${local.backup_kms_service} key ${local.backup_kms_key_id} from the instance GUID ${local.backup_kms_key_instance_guid}" + resource_attributes { + name = "serviceName" + operator = "stringEquals" + value = local.backup_kms_service + } + resource_attributes { + name = "accountId" + operator = "stringEquals" + value = local.backup_kms_account_id + } + resource_attributes { + name = "serviceInstance" + operator = "stringEquals" + value = local.backup_kms_key_instance_guid + } + resource_attributes { + name = "resourceType" + operator = "stringEquals" + value = "key" + } + resource_attributes { + name = "resource" + operator = "stringEquals" + value = local.backup_kms_key_id + } + # Scope of policy now includes the key, so ensure to create new policy before + # destroying old one to prevent any disruption to every day services. + lifecycle { + create_before_destroy = true + } +} + +# workaround for https://github.com/IBM-Cloud/terraform-provider-ibm/issues/4478 +resource "time_sleep" "wait_for_backup_kms_authorization_policy" { + count = local.create_backup_kms_auth_policy + depends_on = [ibm_iam_authorization_policy.backup_kms_policy] + create_duration = "30s" +} + +######################################################################################################################## +# MongoDB instance +######################################################################################################################## + resource "ibm_database" "mongodb" { depends_on = [time_sleep.wait_for_authorization_policy] name = var.instance_name diff --git a/modules/fscloud/README.md b/modules/fscloud/README.md index 84aa5358..0f3a6b57 100644 --- a/modules/fscloud/README.md +++ b/modules/fscloud/README.md @@ -34,13 +34,12 @@ No resources. | [admin\_pass](#input\_admin\_pass) | The password for the database administrator. If the admin password is null then the admin user ID cannot be accessed. More users can be specified in a user block. | `string` | `null` | no | | [auto\_scaling](#input\_auto\_scaling) | Optional rules to allow the database to increase resources in response to usage. Only a single autoscaling block is allowed. Make sure you understand the effects of autoscaling, especially for production environments. See https://cloud.ibm.com/docs/databases-for-mongodb?topic=databases-for-mongodb-autoscaling&interface=cli#autoscaling-considerations in the IBM Cloud Docs. |
object({
disk = object({
capacity_enabled = optional(bool, false)
free_space_less_than_percent = optional(number, 10)
io_above_percent = optional(number, 90)
io_enabled = optional(bool, false)
io_over_period = optional(string, "15m")
rate_increase_percent = optional(number, 10)
rate_limit_mb_per_member = optional(number, 3670016)
rate_period_seconds = optional(number, 900)
rate_units = optional(string, "mb")
})
memory = object({
io_above_percent = optional(number, 90)
io_enabled = optional(bool, false)
io_over_period = optional(string, "15m")
rate_increase_percent = optional(number, 10)
rate_limit_mb_per_member = optional(number, 114688)
rate_period_seconds = optional(number, 900)
rate_units = optional(string, "mb")
})
})
| `null` | no | | [backup\_crn](#input\_backup\_crn) | The CRN of a backup resource to restore from. The backup is created by a database deployment with the same service ID. The backup is loaded after provisioning and the new deployment starts up that uses that data. A backup CRN is in the format crn:v1:<…>:backup:. If omitted, the database is provisioned empty. | `string` | `null` | no | -| [backup\_encryption\_key\_crn](#input\_backup\_encryption\_key\_crn) | The CRN of a Hyper Protect Crypto Services use for encrypting the disk that holds deployment backups. Only used if var.kms\_encryption\_enabled is set to true. There are limitation per region on the Hyper Protect Crypto Services and region for those services. See https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups | `string` | `null` | no | +| [backup\_encryption\_key\_crn](#input\_backup\_encryption\_key\_crn) | The CRN of a Key Protect or Hyper Protect Crypto Services encryption key that you want to use for encrypting the disk that holds deployment backups. Applies only if `use_ibm_owned_encryption_key` is false and `use_same_kms_key_for_backups` is false. If no value is passed, and `use_same_kms_key_for_backups` is true, the value of `kms_key_crn` is used. Alternatively set `use_default_backup_encryption_key` to true to use the IBM Cloud Databases default encryption. Bare in mind that backups encryption is only available in certain regions. See [Bring your own key for backups](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok) and [Using the HPCS Key for Backup encryption](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups). | `string` | `null` | no | | [cbr\_rules](#input\_cbr\_rules) | (Optional, list) List of CBR rules to create |
list(object({
description = string
account_id = string
rule_contexts = list(object({
attributes = optional(list(object({
name = string
value = string
}))) }))
enforcement_mode = string
}))
| `[]` | no | | [cpu\_count](#input\_cpu\_count) | Allocated dedicated CPU per member. For shared CPU, set to 0. [Learn more](https://cloud.ibm.com/docs/databases-for-mongodb?topic=databases-for-mongodb-pricing#mongodb-scale-member) | `number` | `6` | no | | [disk\_mb](#input\_disk\_mb) | Allocated disk per member. [Learn more](https://cloud.ibm.com/docs/databases-for-mongodb?topic=databases-for-mongodb-pricing#mongodb-scale-member) | `number` | `20480` | no | -| [existing\_kms\_instance\_guid](#input\_existing\_kms\_instance\_guid) | The GUID of the Hyper Protect Crypto Services instance. | `string` | n/a | yes | | [instance\_name](#input\_instance\_name) | Name of the mongodb instance | `string` | n/a | yes | -| [kms\_key\_crn](#input\_kms\_key\_crn) | The root key CRN of the Hyper Protect Crypto Services (HPCS) to use for disk encryption. | `string` | n/a | yes | +| [kms\_key\_crn](#input\_kms\_key\_crn) | The CRN of a Key Protect or Hyper Protect Crypto Services encryption key to encrypt your data. Applies only if `use_ibm_owned_encryption_key` is false. By default this key is used for both deployment data and backups, but this behaviour can be altered using the `use_same_kms_key_for_backups` and `backup_encryption_key_crn` inputs. Bare in mind that backups encryption is only available in certain regions. See [Bring your own key for backups](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok) and [Using the HPCS Key for Backup encryption](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups). | `string` | `null` | no | | [member\_host\_flavor](#input\_member\_host\_flavor) | Allocated host flavor per member. [Learn more](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/database#host_flavor). | `string` | `null` | no | | [members](#input\_members) | Allocated number of members | `number` | `3` | no | | [memory\_mb](#input\_memory\_mb) | Allocated memory per member. [Learn more](https://cloud.ibm.com/docs/databases-for-mongodb?topic=databases-for-mongodb-pricing#mongodb-scale-member) | `number` | `14336` | no | @@ -49,8 +48,11 @@ No resources. | [region](#input\_region) | The region where you want to deploy your instance. Must be the same region as the Hyper Protect Crypto Services instance. | `string` | `"us-south"` | no | | [resource\_group\_id](#input\_resource\_group\_id) | The resource group ID where the MongoDB instance will be created. | `string` | n/a | yes | | [service\_credential\_names](#input\_service\_credential\_names) | Map of name, role for service credentials that you want to create for the database | `map(string)` | `{}` | no | -| [skip\_iam\_authorization\_policy](#input\_skip\_iam\_authorization\_policy) | Set to true to skip the creation of an IAM authorization policy that permits all MongoDB database instances in the resource group to read the encryption key from the Hyper Protect Crypto Services instance. The HPCS instance is passed in through the var.existing\_kms\_instance\_guid variable. | `bool` | `false` | no | +| [skip\_iam\_authorization\_policy](#input\_skip\_iam\_authorization\_policy) | Set to true to skip the creation of IAM authorization policies that permits all Databases for MongoDB instances in the given resource group 'Reader' access to the Key Protect or Hyper Protect Crypto Services key that was provided in the `kms_key_crn` and `backup_encryption_key_crn` inputs. This policy is required in order to enable KMS encryption, so only skip creation if there is one already present in your account. No policy is created if `use_ibm_owned_encryption_key` is true. | `bool` | `false` | no | | [tags](#input\_tags) | Optional list of tags to be added to the MongoDB instance. | `list(any)` | `[]` | no | +| [use\_default\_backup\_encryption\_key](#input\_use\_default\_backup\_encryption\_key) | When `use_ibm_owned_encryption_key` is set to false, backups will be encrypted with either the key specified in `kms_key_crn`, or in `backup_encryption_key_crn` if a value is passed. If you do not want to use your own key for backups encryption, you can set this to `true` to use the IBM Cloud Databases default encryption for backups. Alternatively set `use_ibm_owned_encryption_key` to true to use the default encryption for both backups and deployment data. | `bool` | `false` | no | +| [use\_ibm\_owned\_encryption\_key](#input\_use\_ibm\_owned\_encryption\_key) | Set to true to use the default IBM Cloud® Databases randomly generated keys for disk and backups encryption. To control the encryption keys, use the `kms_key_crn` and `backup_encryption_key_crn` inputs. | `string` | `false` | no | +| [use\_same\_kms\_key\_for\_backups](#input\_use\_same\_kms\_key\_for\_backups) | Set this to false if you wan't to use a different key that you own to encrypt backups. When set to false, a value is required for the `backup_encryption_key_crn` input. Alternatiely set `use_default_backup_encryption_key` to true to use the IBM Cloud Databases default encryption. Applies only if `use_ibm_owned_encryption_key` is false. Bare in mind that backups encryption is only available in certain regions. See [Bring your own key for backups](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok) and [Using the HPCS Key for Backup encryption](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups). | `bool` | `true` | no | | [users](#input\_users) | A list of users that you want to create on the database. Multiple blocks are allowed. The user password must be in the range of 10-32 characters. Be warned that in most case using IAM service credentials (via the var.service\_credential\_names) is sufficient to control access to the MongoDB instance. This blocks creates native MongoDB database users, more info on that can be found here https://cloud.ibm.com/docs/databases-for-mongodb?topic=databases-for-mongodb-user-management&interface=ui |
list(object({
name = string
password = string # pragma: allowlist secret
type = optional(string)
role = optional(string)
}))
| `[]` | no | ### Outputs diff --git a/modules/fscloud/main.tf b/modules/fscloud/main.tf index 64ccabec..a1c57152 100644 --- a/modules/fscloud/main.tf +++ b/modules/fscloud/main.tf @@ -1,27 +1,28 @@ module "mongodb" { - source = "../../" - resource_group_id = var.resource_group_id - instance_name = var.instance_name - region = var.region - skip_iam_authorization_policy = var.skip_iam_authorization_policy - endpoints = "private" - mongodb_version = var.mongodb_version - kms_encryption_enabled = true - existing_kms_instance_guid = var.existing_kms_instance_guid - kms_key_crn = var.kms_key_crn - backup_encryption_key_crn = var.backup_encryption_key_crn - cbr_rules = var.cbr_rules - access_tags = var.access_tags - tags = var.tags - plan = var.plan - members = var.members - memory_mb = var.memory_mb - admin_pass = var.admin_pass - users = var.users - disk_mb = var.disk_mb - cpu_count = var.cpu_count - member_host_flavor = var.member_host_flavor - auto_scaling = var.auto_scaling - service_credential_names = var.service_credential_names - backup_crn = var.backup_crn + source = "../../" + resource_group_id = var.resource_group_id + instance_name = var.instance_name + region = var.region + skip_iam_authorization_policy = var.skip_iam_authorization_policy + endpoints = "private" + mongodb_version = var.mongodb_version + use_ibm_owned_encryption_key = var.use_ibm_owned_encryption_key + use_same_kms_key_for_backups = var.use_same_kms_key_for_backups + use_default_backup_encryption_key = var.use_default_backup_encryption_key + kms_key_crn = var.kms_key_crn + backup_crn = var.backup_crn + backup_encryption_key_crn = var.backup_encryption_key_crn + cbr_rules = var.cbr_rules + access_tags = var.access_tags + tags = var.tags + plan = var.plan + members = var.members + memory_mb = var.memory_mb + admin_pass = var.admin_pass + users = var.users + disk_mb = var.disk_mb + cpu_count = var.cpu_count + member_host_flavor = var.member_host_flavor + auto_scaling = var.auto_scaling + service_credential_names = var.service_credential_names } diff --git a/modules/fscloud/variables.tf b/modules/fscloud/variables.tf index 29fe1606..34999377 100644 --- a/modules/fscloud/variables.tf +++ b/modules/fscloud/variables.tf @@ -136,26 +136,56 @@ variable "auto_scaling" { # Encryption ############################################################## +variable "use_ibm_owned_encryption_key" { + type = string + description = "Set to true to use the default IBM Cloud® Databases randomly generated keys for disk and backups encryption. To control the encryption keys, use the `kms_key_crn` and `backup_encryption_key_crn` inputs." + default = false +} + variable "kms_key_crn" { type = string - description = "The root key CRN of the Hyper Protect Crypto Services (HPCS) to use for disk encryption." + description = "The CRN of a Key Protect or Hyper Protect Crypto Services encryption key to encrypt your data. Applies only if `use_ibm_owned_encryption_key` is false. By default this key is used for both deployment data and backups, but this behaviour can be altered using the `use_same_kms_key_for_backups` and `backup_encryption_key_crn` inputs. Bare in mind that backups encryption is only available in certain regions. See [Bring your own key for backups](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok) and [Using the HPCS Key for Backup encryption](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups)." + default = null + validation { + condition = anytrue([ + var.kms_key_crn == null, + can(regex(".*kms.*", var.kms_key_crn)), + can(regex(".*hs-crypto.*", var.kms_key_crn)), + ]) + error_message = "Value must be the KMS key CRN from a Key Protect or Hyper Protect Crypto Services instance." + } +} + +variable "use_same_kms_key_for_backups" { + type = bool + description = "Set this to false if you wan't to use a different key that you own to encrypt backups. When set to false, a value is required for the `backup_encryption_key_crn` input. Alternatiely set `use_default_backup_encryption_key` to true to use the IBM Cloud Databases default encryption. Applies only if `use_ibm_owned_encryption_key` is false. Bare in mind that backups encryption is only available in certain regions. See [Bring your own key for backups](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok) and [Using the HPCS Key for Backup encryption](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups)." + default = true } variable "backup_encryption_key_crn" { type = string - description = "The CRN of a Hyper Protect Crypto Services use for encrypting the disk that holds deployment backups. Only used if var.kms_encryption_enabled is set to true. There are limitation per region on the Hyper Protect Crypto Services and region for those services. See https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups" + description = "The CRN of a Key Protect or Hyper Protect Crypto Services encryption key that you want to use for encrypting the disk that holds deployment backups. Applies only if `use_ibm_owned_encryption_key` is false and `use_same_kms_key_for_backups` is false. If no value is passed, and `use_same_kms_key_for_backups` is true, the value of `kms_key_crn` is used. Alternatively set `use_default_backup_encryption_key` to true to use the IBM Cloud Databases default encryption. Bare in mind that backups encryption is only available in certain regions. See [Bring your own key for backups](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok) and [Using the HPCS Key for Backup encryption](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups)." default = null + validation { + condition = anytrue([ + var.backup_encryption_key_crn == null, + can(regex(".*kms.*", var.backup_encryption_key_crn)), + can(regex(".*hs-crypto.*", var.backup_encryption_key_crn)), + ]) + error_message = "Value must be the KMS key CRN from a Key Protect or Hyper Protect Crypto Services instance in one of the supported backup regions." + } } -variable "skip_iam_authorization_policy" { +variable "use_default_backup_encryption_key" { type = bool - description = "Set to true to skip the creation of an IAM authorization policy that permits all MongoDB database instances in the resource group to read the encryption key from the Hyper Protect Crypto Services instance. The HPCS instance is passed in through the var.existing_kms_instance_guid variable." + description = "When `use_ibm_owned_encryption_key` is set to false, backups will be encrypted with either the key specified in `kms_key_crn`, or in `backup_encryption_key_crn` if a value is passed. If you do not want to use your own key for backups encryption, you can set this to `true` to use the IBM Cloud Databases default encryption for backups. Alternatively set `use_ibm_owned_encryption_key` to true to use the default encryption for both backups and deployment data." default = false } -variable "existing_kms_instance_guid" { - type = string - description = "The GUID of the Hyper Protect Crypto Services instance." +variable "skip_iam_authorization_policy" { + type = bool + description = "Set to true to skip the creation of IAM authorization policies that permits all Databases for MongoDB instances in the given resource group 'Reader' access to the Key Protect or Hyper Protect Crypto Services key that was provided in the `kms_key_crn` and `backup_encryption_key_crn` inputs. This policy is required in order to enable KMS encryption, so only skip creation if there is one already present in your account. No policy is created if `use_ibm_owned_encryption_key` is true." + default = false } ############################################################## diff --git a/reference-architecture/deployable-architecture-mongodb.svg b/reference-architecture/deployable-architecture-mongodb.svg new file mode 100644 index 00000000..9e1a3b0d --- /dev/null +++ b/reference-architecture/deployable-architecture-mongodb.svg @@ -0,0 +1,4 @@ + + + +
IBM Cloud
IBM Cloud
KMS Encryption
KMS Encryption
Region
Region
Resource Group
Resource Group
IBM Cloud MongoDB Instance
IBM Cloud MongoDB Instance
MDB
MDB
Text is not SVG - cannot display
\ No newline at end of file diff --git a/solutions/standard/DA-types.md b/solutions/standard/DA-types.md new file mode 100644 index 00000000..63ff96f5 --- /dev/null +++ b/solutions/standard/DA-types.md @@ -0,0 +1,203 @@ +# Configuring complex inputs in Databases for MongoDB + +Several optional input variables in the IBM Cloud [Databases for MongoDB deployable architecture](https://cloud.ibm.com/catalog#deployable_architecture) use complex object types. You specify these inputs when you configure deployable architecture. + +- [Service credentials](#svc-credential-name) (`service_credential_names`) +- [Service credential secrets](#service-credential-secrets) (`service_credential_secrets`) +- [Users](#users) (`users`) +- [Autoscaling](#autoscaling) (`auto_scaling`) + +## Service credentials + +You can specify a set of IAM credentials to connect to the database with the `service_credential_names` input variable. Include a credential name and IAM service role for each key-value pair. Each role provides a specific level of access to the database. For more information, see [Adding and viewing credentials](https://cloud.ibm.com/docs/account?topic=account-service_credentials&interface=ui). If you want to add service credentials to secret manager and to allow secret manager to manage it, you should use `service_credential_secrets` , see [Service credential secrets](#service-credential-secrets) + +- Variable name: `service_credential_names`. +- Type: A map. The key is the name of the service credential. The value is the role that is assigned to that credential. +- Default value: An empty map (`{}`). + +### Options for service_credential_names + +- Key (required): The name of the service credential. +- Value (required): The IAM service role that is assigned to the credential. The following values are valid for service credential roles: "Administrator", "Operator", "Viewer" and "Editor". For more information, see [IBM Cloud IAM roles](https://cloud.ibm.com/docs/account?topic=account-userroles). + +### Example service credential + +```hcl + { + "mongodb_admin" : "Administrator", + "mongodb_reader" : "Operator", + "mongodb_viewer" : "Viewer", + "mongodb_editor" : "Editor" + } +``` + +## Service credential secrets + +When you add an IBM Database for MongoDB deployable architecture from the IBM Cloud catalog to IBM Cloud Project , you can configure service credentials. In edit mode for the projects configuration, from the configure panel click the optional tab. + +To enter a custom value, use the edit action to open the "Edit Array" panel. Add the service credential secrets configurations to the array here. + +In the configuration, specify the secret group name, whether it already exists or will be created and include all the necessary service credential secrets that need to be created within that secret group. + + [Learn more](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/data-sources/sm_service_credentials_secret) about service credential secrets. + +- Variable name: `service_credential_secrets`. +- Type: A list of objects that represent a service credential secret groups and secrets +- Default value: An empty list (`[]`) + +### Options for service_credential_secrets + +- `secret_group_name` (required): A unique human-readable name that identifies this service credential secret group. +- `secret_group_description` (optional, default = `null`): A human-readable description for this secret group. +- `existing_secret_group`: (optional, default = `false`): Set to true, if secret group name provided in the variable `secret_group_name` already exists. +- `service_credentials`: (optional, default = `[]`): A list of object that represents a service credential secret. + +#### Options for service_credentials + +- `secret_name`: (required): A unique human-readable name of the secret to create. +- `service_credentials_source_service_role`: (required): The role to give the service credential in the Databases for MongoDB service. Acceptable values are `Writer`, `Reader`, `Manager`, and `None` +- `secret_labels`: (optional, default = `[]`): 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 (|). +- `secret_auto_rotation`: (optional, default = `true`): Whether to configure automatic rotation of service credential. +- `secret_auto_rotation_unit`: (optional, default = `day`): Specifies the unit of time for rotation of a secret. Acceptable values are `day` or `month`. +- `secret_auto_rotation_interval`: (optional, default = `89`): Specifies the rotation interval for the rotation unit. +- `service_credentials_ttl`: (optional, default = `7776000`): The time-to-live (TTL) to assign to generated service credentials (in seconds). +- `service_credential_secret_description`: (optional, default = `null`): Description of the secret to create. + +The following example includes all the configuration options for four service credentials and two secret groups. +```hcl +[ + { + "secret_group_name": "sg-1" + "existing_secret_group": true + "service_credentials": [ # pragma: allowlist secret + { + "secret_name": "cred-1" + "service_credentials_source_service_role": "Writer" + "secret_labels": ["test-writer-1", "test-writer-2"] + "secret_auto_rotation": true + "secret_auto_rotation_unit": "day" + "secret_auto_rotation_interval": 89 + "service_credentials_ttl": 7776000 + "service_credential_secret_description": "sample description" + }, + { + "secret_name": "cred-2" + "service_credentials_source_service_role": "Reader" + } + ] + }, + { + "secret_group_name": "sg-2" + "service_credentials": [ # pragma: allowlist secret + { + "secret_name": "cred-3" + "service_credentials_source_service_role": "Editor" + }, + { + "secret_name": "cred-4" + "service_credentials_source_service_role": "None" + } + ] + } +] +``` + +## Users + +If you can't use the IAM-enabled `service_credential_names` input variable for access, you can create users and roles directly in the database. For more information, see [Managing users and roles](https://cloud.ibm.com/docs/databases-for-mongodb?topic=databases-for-mongodb-user-management&interface=ui). + +:exclamation: **Important:** The `users` input contains sensitive information (the user's password). + +- Variable name: `users`. +- Type: A list of objects that represent a user +- Default value: An empty list (`[]`) + +### Options for users + + - `name` (required): The username for the user account. + - `password` (required): The password for the user account in the range of 10-32 characters. + - `type` (required): The user type. The "type" field is required to generate the connection string for the outputs. + - `role`: The user role. The role determines the user's access level and permissions. + +### Example users + + +```hcl +[ + { + "name": "es_admin", + "password": "securepassword123", # pragma: allowlist secret + "type": "database", + }, + { + "name": "es_reader", + "password": "readpassword123", # pragma: allowlist secret + "type": "ops_manager" + } +] +``` + +## Autoscaling + +The Autoscaling variable sets the rules for how database increase resources in response to usage. Make sure you understand the effects of autoscaling, especially for production environments. For more information, see [Autoscaling](https://cloud.ibm.com/docs/databases-for-mongodb?topic=databases-for-mongodb-autoscaling&interface=ui#autoscaling-consider). + +- Variable name: `auto_scaling` +- Type: An object with `disk` and `memory` configurations. + +### Disk options for auto_scaling + +Disk autoscaling specifies thresholds when scaling can occur based on disk usage, disk I/O utilization, or both. + +The disk object in the `auto_scaling` input contains the following options. All options are optional. + +- `capacity_enabled`: Whether disk capacity autoscaling is enabled (default: `false`). +- `free_space_less_than_percent`: The percentage of free disk space that triggers autoscaling (default: `10`). +- `io_above_percent`: The percentage of I/O (input/output) disk usage that triggers autoscaling (default: `90`). +- `io_enabled`: Indicates whether IO-based autoscaling is enabled (default: `false`). +- `io_over_period`: How long I/O usage is evaluated for autoscaling (default: `"15m"` (15 minutes)). +- `rate_increase_percent`: The percentage increase in disk capacity when autoscaling is triggered (default: `10`). +- `rate_limit_mb_per_member`: The limit in megabytes for the rate of disk increase per member (default: `3670016`). +- `rate_period_seconds`: How long (in seconds) the rate limit is applied for disk (default: `900` (15 minutes)). +- `rate_units`: The units to use for the rate increase (default: `"mb"` (megabytes)). + + +### Memory options for auto_scaling + +The memory object within auto_scaling contains the following options. All options are optional. + +- `io_above_percent`: The percentage of I/O memory usage that triggers autoscaling (default: `90`). +- `io_enabled`: Whether IO-based autoscaling for memory is enabled (default: `false`). +- `io_over_period`: How long I/O usage is evaluated for memory autoscaling (default: `"15m"` (15 minutes)). +- `rate_increase_percent`: The percentage increase in memory capacity that triggers autoscaling (default: `10`). +- `rate_limit_mb_per_member`: The limit in megabytes for the rate of memory increase per member (default: `114688`). +- `rate_period_seconds`: How long (in seconds) the rate limit is applied for memory (default: `900` (15 minutes)). +- `rate_units`: The memory size units to use for the rate increase (default: `"mb"` (megabytes)). + +### Example autoscaling + +The following example shows values for both disk and memory for the `auto_scaling` input. + +```hcl +{ + "disk": { + "capacity_enabled": true, + "free_space_less_than_percent": 15, + "io_above_percent": 85, + "io_enabled": true, + "io_over_period": "15m", + "rate_increase_percent": 15, + "rate_limit_mb_per_member": 3670016, + "rate_period_seconds": 900, + "rate_units": "mb" + }, + "memory": { + "io_above_percent": 90, + "io_enabled": true, + "io_over_period": "15m", + "rate_increase_percent": 10, + "rate_limit_mb_per_member": 114688, + "rate_period_seconds": 900, + "rate_units": "mb" + } +} +``` diff --git a/solutions/standard/README.md b/solutions/standard/README.md new file mode 100644 index 00000000..8a4c8a55 --- /dev/null +++ b/solutions/standard/README.md @@ -0,0 +1,13 @@ + # IBM Cloud Databases for MongoDB + +This architecture creates an instance of IBM Cloud Databases for MongoDB and supports provisioning of the following resources: + +- A resource group, if one is not passed in. +- A KMS root key, if one is not passed in. +- An IBM Cloud Databases for MongoDB instance with KMS encryption. +- Autoscaling rules for the database instance, if provided. +- Service credential secrets and store them in secret manager. + +![fscloud-mongodb](../../reference-architecture/deployable-architecture-mongodb.svg) + +: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/standard/catalogValidationValues.json.template b/solutions/standard/catalogValidationValues.json.template new file mode 100644 index 00000000..e69e502f --- /dev/null +++ b/solutions/standard/catalogValidationValues.json.template @@ -0,0 +1,8 @@ +{ + "ibmcloud_api_key": $VALIDATION_APIKEY, + "region": "us-south", + "tags": $TAGS, + "name": $PREFIX, + "resource_group_name": $PREFIX, + "existing_kms_instance_crn": $HPCS_US_SOUTH_CRN +} diff --git a/solutions/standard/main.tf b/solutions/standard/main.tf new file mode 100644 index 00000000..a7fcc40d --- /dev/null +++ b/solutions/standard/main.tf @@ -0,0 +1,337 @@ +####################################################################################################################### +# Resource Group +####################################################################################################################### + +module "resource_group" { + source = "terraform-ibm-modules/resource-group/ibm" + version = "1.1.6" + resource_group_name = var.use_existing_resource_group == false ? (var.prefix != null ? "${var.prefix}-${var.resource_group_name}" : var.resource_group_name) : null + existing_resource_group_name = var.use_existing_resource_group == true ? var.resource_group_name : null +} + +####################################################################################################################### +# KMS related variable validation +# (approach based on https://github.com/hashicorp/terraform/issues/25609#issuecomment-1057614400) +# +# TODO: Replace with terraform cross variable validation: https://github.ibm.com/GoldenEye/issues/issues/10836 +####################################################################################################################### + +locals { + # tflint-ignore: terraform_unused_declarations + validate_kms_1 = var.use_ibm_owned_encryption_key && (var.existing_kms_instance_crn != null || var.existing_kms_key_crn != null || var.existing_backup_kms_key_crn != null) ? tobool("When setting values for 'existing_kms_instance_crn', 'existing_kms_key_crn' or 'existing_backup_kms_key_crn', the 'use_ibm_owned_encryption_key' input must be set to false.") : true + # tflint-ignore: terraform_unused_declarations + validate_kms_2 = !var.use_ibm_owned_encryption_key && (var.existing_kms_instance_crn == null && var.existing_kms_key_crn == null) ? tobool("When 'use_ibm_owned_encryption_key' is false, a value is required for either 'existing_kms_instance_crn' (to create a new key), or 'existing_kms_key_crn' to use an existing key.") : true +} + +####################################################################################################################### +# KMS encryption key +####################################################################################################################### + +locals { + create_new_kms_key = !var.use_ibm_owned_encryption_key && var.existing_kms_key_crn == null ? true : false # no need to create any KMS resources if passing an existing key, or using IBM owned keys + mongodb_key_name = var.prefix != null ? "${var.prefix}-${var.key_name}" : var.key_name + mongodb_key_ring_name = var.prefix != null ? "${var.prefix}-${var.key_ring_name}" : var.key_ring_name +} + +module "kms" { + providers = { + ibm = ibm.kms + } + count = local.create_new_kms_key ? 1 : 0 + source = "terraform-ibm-modules/kms-all-inclusive/ibm" + version = "4.19.2" + create_key_protect_instance = false + region = local.kms_region + existing_kms_instance_crn = var.existing_kms_instance_crn + key_ring_endpoint_type = var.kms_endpoint_type + key_endpoint_type = var.kms_endpoint_type + keys = [ + { + key_ring_name = local.mongodb_key_ring_name + existing_key_ring = false + keys = [ + { + key_name = local.mongodb_key_name + standard_key = false + rotation_interval_month = 3 + dual_auth_delete_enabled = false + force_delete = true + } + ] + } + ] +} + +######################################################################################################################## +# Parse KMS info from given CRNs +######################################################################################################################## + +module "kms_instance_crn_parser" { + count = var.existing_kms_instance_crn != null ? 1 : 0 + source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser" + version = "1.1.0" + crn = var.existing_kms_instance_crn +} + +module "kms_key_crn_parser" { + count = var.existing_kms_key_crn != null ? 1 : 0 + source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser" + version = "1.1.0" + crn = var.existing_kms_key_crn +} + +module "kms_backup_key_crn_parser" { + count = var.existing_backup_kms_key_crn != null ? 1 : 0 + source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser" + version = "1.1.0" + crn = var.existing_backup_kms_key_crn +} + +####################################################################################################################### +# KMS IAM Authorization Policies +# - only created if user passes a value for 'ibmcloud_kms_api_key' (used when KMS is in different account to MongoDB) +# - if no value passed for 'ibmcloud_kms_api_key', the auth policy is created by the MongoDB module +####################################################################################################################### + +# Lookup account ID +data "ibm_iam_account_settings" "iam_account_settings" { +} + +locals { + account_id = data.ibm_iam_account_settings.iam_account_settings.account_id + create_cross_account_kms_auth_policy = !var.skip_mongodb_kms_auth_policy && var.ibmcloud_kms_api_key != null && !var.use_ibm_owned_encryption_key + create_cross_account_backup_kms_auth_policy = !var.skip_mongodb_kms_auth_policy && var.ibmcloud_kms_api_key != null && !var.use_ibm_owned_encryption_key && var.existing_backup_kms_key_crn != null + + # If KMS encryption enabled (and existing ES instance is not being passed), parse details from the existing key if being passed, otherwise get it from the key that the DA creates + kms_account_id = var.use_ibm_owned_encryption_key ? null : var.existing_kms_key_crn != null ? module.kms_key_crn_parser[0].account_id : module.kms_instance_crn_parser[0].account_id + kms_service = var.use_ibm_owned_encryption_key ? null : var.existing_kms_key_crn != null ? module.kms_key_crn_parser[0].service_name : module.kms_instance_crn_parser[0].service_name + kms_instance_guid = var.use_ibm_owned_encryption_key ? null : var.existing_kms_key_crn != null ? module.kms_key_crn_parser[0].service_instance : module.kms_instance_crn_parser[0].service_instance + kms_key_crn = var.use_ibm_owned_encryption_key ? null : var.existing_kms_key_crn != null ? var.existing_kms_key_crn : module.kms[0].keys[format("%s.%s", local.mongodb_key_ring_name, local.mongodb_key_name)].crn + kms_key_id = var.use_ibm_owned_encryption_key ? null : var.existing_kms_key_crn != null ? module.kms_key_crn_parser[0].resource : module.kms[0].keys[format("%s.%s", local.mongodb_key_ring_name, local.mongodb_key_name)].key_id + kms_region = var.use_ibm_owned_encryption_key ? null : var.existing_kms_key_crn != null ? module.kms_key_crn_parser[0].region : module.kms_instance_crn_parser[0].region + + # If creating KMS cross account policy for backups, parse backup key details from passed in key CRN + backup_kms_account_id = local.create_cross_account_backup_kms_auth_policy ? module.kms_backup_key_crn_parser[0].account_id : local.kms_account_id + backup_kms_service = local.create_cross_account_backup_kms_auth_policy ? module.kms_backup_key_crn_parser[0].service_name : local.kms_service + backup_kms_instance_guid = local.create_cross_account_backup_kms_auth_policy ? module.kms_backup_key_crn_parser[0].service_instance : local.kms_instance_guid + backup_kms_key_id = local.create_cross_account_backup_kms_auth_policy ? module.kms_backup_key_crn_parser[0].resource : local.kms_key_id + backup_kms_key_crn = var.use_ibm_owned_encryption_key ? null : var.existing_backup_kms_key_crn + # Always use same key for backups unless user explicially passed a value for 'existing_backup_kms_key_crn' + use_same_kms_key_for_backups = var.existing_backup_kms_key_crn == null ? true : false +} + +# Create auth policy (scoped to exact KMS key) +resource "ibm_iam_authorization_policy" "kms_policy" { + count = local.create_cross_account_kms_auth_policy ? 1 : 0 + provider = ibm.kms + source_service_account = local.account_id + source_service_name = "databases-for-mongodb" + source_resource_group_id = module.resource_group.resource_group_id + roles = ["Reader"] + description = "Allow all MongoDB instances in the resource group ${module.resource_group.resource_group_id} in the account ${local.account_id} to read the ${local.kms_service} key ${local.kms_key_id} from the instance GUID ${local.kms_instance_guid}" + resource_attributes { + name = "serviceName" + operator = "stringEquals" + value = local.kms_service + } + resource_attributes { + name = "accountId" + operator = "stringEquals" + value = local.kms_account_id + } + resource_attributes { + name = "serviceInstance" + operator = "stringEquals" + value = local.kms_instance_guid + } + resource_attributes { + name = "resourceType" + operator = "stringEquals" + value = "key" + } + resource_attributes { + name = "resource" + operator = "stringEquals" + value = local.kms_key_id + } + # Scope of policy now includes the key, so ensure to create new policy before + # destroying old one to prevent any disruption to every day services. + lifecycle { + create_before_destroy = true + } +} + +# workaround for https://github.com/IBM-Cloud/terraform-provider-ibm/issues/4478 +resource "time_sleep" "wait_for_authorization_policy" { + count = local.create_cross_account_kms_auth_policy ? 1 : 0 + depends_on = [ibm_iam_authorization_policy.kms_policy] + create_duration = "30s" +} + +# Create auth policy (scoped to exact KMS key for backups) +resource "ibm_iam_authorization_policy" "backup_kms_policy" { + count = local.create_cross_account_backup_kms_auth_policy ? 1 : 0 + provider = ibm.kms + source_service_account = local.account_id + source_service_name = "databases-for-mongodb" + source_resource_group_id = module.resource_group.resource_group_id + roles = ["Reader"] + description = "Allow all MongoDB instances in the resource group ${module.resource_group.resource_group_id} in the account ${local.account_id} to read the ${local.backup_kms_service} key ${local.backup_kms_key_id} from the instance GUID ${local.backup_kms_instance_guid}" + resource_attributes { + name = "serviceName" + operator = "stringEquals" + value = local.backup_kms_service + } + resource_attributes { + name = "accountId" + operator = "stringEquals" + value = local.backup_kms_account_id + } + resource_attributes { + name = "serviceInstance" + operator = "stringEquals" + value = local.backup_kms_instance_guid + } + resource_attributes { + name = "resourceType" + operator = "stringEquals" + value = "key" + } + resource_attributes { + name = "resource" + operator = "stringEquals" + value = local.backup_kms_key_id + } + # Scope of policy now includes the key, so ensure to create new policy before + # destroying old one to prevent any disruption to every day services. + lifecycle { + create_before_destroy = true + } +} + +# workaround for https://github.com/IBM-Cloud/terraform-provider-ibm/issues/4478 +resource "time_sleep" "wait_for_backup_kms_authorization_policy" { + count = local.create_cross_account_backup_kms_auth_policy ? 1 : 0 + depends_on = [ibm_iam_authorization_policy.backup_kms_policy] + create_duration = "30s" +} + +####################################################################################################################### +# MongoDB admin password +####################################################################################################################### + +resource "random_password" "admin_password" { + count = var.admin_pass == null ? 1 : 0 + length = 32 + special = true + override_special = "-_" + min_numeric = 1 +} + +locals { + # _- are invalid first characters + # if - replace first char with J + # elseif _ replace first char with K + # else use asis + generated_admin_password = startswith(random_password.admin_password[0].result, "-") ? "J${substr(random_password.admin_password[0].result, 1, -1)}" : startswith(random_password.admin_password[0].result, "_") ? "K${substr(random_password.admin_password[0].result, 1, -1)}" : random_password.admin_password[0].result + + # admin password to use + admin_pass = var.admin_pass == null ? local.generated_admin_password : var.admin_pass +} + +####################################################################################################################### +# MongoDB +####################################################################################################################### + +# Create new instance +module "mongodb" { + source = "../../modules/fscloud" + depends_on = [time_sleep.wait_for_authorization_policy, time_sleep.wait_for_backup_kms_authorization_policy] + resource_group_id = module.resource_group.resource_group_id + instance_name = var.prefix != null ? "${var.prefix}-${var.name}" : var.name + plan = var.plan + region = var.region + mongodb_version = var.mongodb_version + skip_iam_authorization_policy = var.skip_mongodb_kms_auth_policy + use_ibm_owned_encryption_key = var.use_ibm_owned_encryption_key + kms_key_crn = local.kms_key_crn + backup_encryption_key_crn = local.backup_kms_key_crn + use_same_kms_key_for_backups = local.use_same_kms_key_for_backups + use_default_backup_encryption_key = var.use_default_backup_encryption_key + access_tags = var.access_tags + tags = var.tags + admin_pass = local.admin_pass + users = var.users + members = var.members + member_host_flavor = var.member_host_flavor + memory_mb = var.member_memory_mb + disk_mb = var.member_disk_mb + cpu_count = var.member_cpu_count + auto_scaling = var.auto_scaling + service_credential_names = var.service_credential_names + backup_crn = var.backup_crn +} + +locals { + create_sm_auth_policy = var.skip_mongodb_sm_auth_policy || var.existing_secrets_manager_instance_crn == null ? 0 : 1 +} + +# create a service authorization between Secrets Manager and the target service (Databases for MongoDB) +resource "ibm_iam_authorization_policy" "secrets_manager_key_manager" { + count = local.create_sm_auth_policy + source_service_name = "secrets-manager" + source_resource_instance_id = local.existing_secrets_manager_instance_guid + target_service_name = "databases-for-mongodb" + target_resource_instance_id = module.mongodb.guid + roles = ["Key Manager"] + description = "Allow Secrets Manager with instance id ${local.existing_secrets_manager_instance_guid} to manage key for the databases-for-mongodb instance" +} + +# workaround for https://github.com/IBM-Cloud/terraform-provider-ibm/issues/4478 +resource "time_sleep" "wait_for_mongodb_authorization_policy" { + count = local.create_sm_auth_policy + depends_on = [ibm_iam_authorization_policy.secrets_manager_key_manager] + create_duration = "30s" +} + +locals { + service_credential_secrets = [ + for service_credentials in var.service_credential_secrets : { + secret_group_name = service_credentials.secret_group_name + secret_group_description = service_credentials.secret_group_description + existing_secret_group = service_credentials.existing_secret_group + secrets = [ + for secret in service_credentials.service_credentials : { + secret_name = secret.secret_name + secret_labels = secret.secret_labels + secret_auto_rotation = secret.secret_auto_rotation + secret_auto_rotation_unit = secret.secret_auto_rotation_unit + secret_auto_rotation_interval = secret.secret_auto_rotation_interval + service_credentials_ttl = secret.service_credentials_ttl + service_credential_secret_description = secret.service_credential_secret_description + service_credentials_source_service_role = secret.service_credentials_source_service_role + service_credentials_source_service_crn = module.mongodb.crn + secret_type = "service_credentials" #checkov:skip=CKV_SECRET_6 + } + ] + } + ] + + existing_secrets_manager_instance_crn_split = var.existing_secrets_manager_instance_crn != null ? split(":", var.existing_secrets_manager_instance_crn) : null + existing_secrets_manager_instance_guid = var.existing_secrets_manager_instance_crn != null ? element(local.existing_secrets_manager_instance_crn_split, length(local.existing_secrets_manager_instance_crn_split) - 3) : null + existing_secrets_manager_instance_region = var.existing_secrets_manager_instance_crn != null ? element(local.existing_secrets_manager_instance_crn_split, length(local.existing_secrets_manager_instance_crn_split) - 5) : null + + # tflint-ignore: terraform_unused_declarations + validate_sm_crn = length(local.service_credential_secrets) > 0 && var.existing_secrets_manager_instance_crn == null ? tobool("`existing_secrets_manager_instance_crn` is required when adding service credentials to a secrets manager secret.") : false +} + +module "secrets_manager_service_credentials" { + count = length(local.service_credential_secrets) > 0 ? 1 : 0 + depends_on = [time_sleep.wait_for_mongodb_authorization_policy] + source = "terraform-ibm-modules/secrets-manager/ibm//modules/secrets" + version = "1.19.10" + existing_sm_instance_guid = local.existing_secrets_manager_instance_guid + existing_sm_instance_region = local.existing_secrets_manager_instance_region + endpoint_type = var.existing_secrets_manager_endpoint_type + secrets = local.service_credential_secrets +} diff --git a/solutions/standard/outputs.tf b/solutions/standard/outputs.tf new file mode 100644 index 00000000..ae2e4141 --- /dev/null +++ b/solutions/standard/outputs.tf @@ -0,0 +1,66 @@ +############################################################################## +# Outputs +############################################################################## + +output "id" { + description = "MongoDB instance id" + value = module.mongodb.id +} + +output "version" { + description = "MongoDB instance version" + value = module.mongodb.version +} + +output "guid" { + description = "MongoDB instance guid" + value = module.mongodb.guid +} + +output "crn" { + description = "MongoDB instance crn" + value = module.mongodb.crn +} + +output "cbr_rule_ids" { + description = "CBR rule ids created to restrict MongoDB" + value = module.mongodb.cbr_rule_ids +} + +output "service_credentials_json" { + description = "Service credentials json map" + value = module.mongodb.service_credentials_json + sensitive = true +} + +output "service_credentials_object" { + description = "Service credentials object" + value = module.mongodb.service_credentials_object + sensitive = true +} + +output "adminuser" { + description = "Database admin user name" + value = module.mongodb.adminuser +} + +output "hostname" { + description = "Database connection hostname" + value = module.mongodb.hostname +} + +output "port" { + description = "Database connection port" + value = module.mongodb.port +} + +output "certificate_base64" { + description = "Database connection certificate" + value = module.mongodb.certificate_base64 + sensitive = true +} + +output "secrets_manager_secrets" { + description = "Service credential secrets" + value = length(local.service_credential_secrets) > 0 ? module.secrets_manager_service_credentials[0].secrets : null +} diff --git a/solutions/standard/provider.tf b/solutions/standard/provider.tf new file mode 100644 index 00000000..65c38f7d --- /dev/null +++ b/solutions/standard/provider.tf @@ -0,0 +1,11 @@ +provider "ibm" { + ibmcloud_api_key = var.ibmcloud_api_key + region = var.region + visibility = var.provider_visibility +} +provider "ibm" { + alias = "kms" + ibmcloud_api_key = var.ibmcloud_kms_api_key != null ? var.ibmcloud_kms_api_key : var.ibmcloud_api_key + region = local.kms_region + visibility = var.provider_visibility +} diff --git a/solutions/standard/variables.tf b/solutions/standard/variables.tf new file mode 100644 index 00000000..f27c55f9 --- /dev/null +++ b/solutions/standard/variables.tf @@ -0,0 +1,311 @@ +############################################################################## +# Input Variables +############################################################################## + +variable "ibmcloud_api_key" { + type = string + description = "The IBM Cloud API key to deploy resources." + sensitive = true +} +variable "use_existing_resource_group" { + type = bool + description = "Whether to use an existing resource group." + default = false +} + +variable "resource_group_name" { + type = string + description = "The name of a new or an existing resource group to provision the Databases for MongoDB in. If a prefix input variable is specified, the prefix is added to the name in the `-` format." +} + +variable "prefix" { + type = string + description = "Prefix to add to all resources created by this solution." + default = null +} + +variable "name" { + type = string + description = "The name of the Databases for MongoDB instance. If a prefix input variable is specified, the prefix is added to the name in the `-` format." + default = "mongodb" +} + +variable "region" { + description = "The region where you want to deploy your instance." + type = string + default = "us-south" +} + +variable "mongodb_version" { + description = "The version of the Databases for MongoDB instance. If no value is specified, the current preferred version of Databases for MongoDB is used." + type = string + default = null +} + +variable "plan" { + type = string + description = "The name of the service plan that you choose for your MongoDB instance" + default = "standard" + + validation { + condition = anytrue([ + var.plan == "standard", + var.plan == "enterprise", + ]) + error_message = "Only supported plans are standard or enterprise" + } +} + +############################################################################## +# ICD hosting model properties +############################################################################## + +variable "members" { + type = number + description = "The number of members that are allocated. [Learn more](https://cloud.ibm.com/docs/databases-for-mongodb?topic=databases-for-mongodb-resources-scaling)." + default = 3 +} + +variable "member_memory_mb" { + type = number + description = "The memory per member that is allocated. [Learn more](https://cloud.ibm.com/docs/databases-for-mongodb?topic=databases-for-mongodb-resources-scaling)" + default = 4096 +} + +variable "member_cpu_count" { + type = number + description = "The dedicated CPU per member that is allocated. For shared CPU, set to 0. [Learn more](https://cloud.ibm.com/docs/databases-for-mongodb?topic=databases-for-mongodb-resources-scaling)." + default = 0 +} + +variable "member_disk_mb" { + type = number + description = "The disk that is allocated per member. [Learn more](https://cloud.ibm.com/docs/databases-for-mongodb?topic=databases-for-mongodb-resources-scaling)." + default = 10240 +} + +variable "member_host_flavor" { + type = string + description = "The host flavor per member. [Learn more](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/database#host_flavor)." + default = "multitenant" +} + +variable "service_credential_names" { + description = "Map of name, role for service credentials that you want to create for the database. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-icd-mongodb/blob/main/solutions/standard/DA-types.md#svc-credential-name)" + type = map(string) + default = {} +} + +variable "admin_pass" { + type = string + description = "The password for the database administrator. If the admin password is null then the admin user ID cannot be accessed. More users can be specified in a user block." + default = null + sensitive = true +} + +variable "users" { + type = list(object({ + name = string + password = string # pragma: allowlist secret + type = string # "type" is required to generate the connection string for the outputs. + role = optional(string) + })) + default = [] + sensitive = true + description = "A list of users that you want to create on the database. Users block is supported by MongoDB version >= 6.0. Multiple blocks are allowed. The user password must be in the range of 10-32 characters. Be warned that in most case using IAM service credentials (via the var.service_credential_names) is sufficient to control access to the MongoDB instance. This blocks creates native MongoDB database users. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-icd-mongodb/blob/main/solutions/standard/DA-types.md#users)" +} + +variable "tags" { + type = list(any) + description = "The list of tags to be added to the Databases for MongoDB instance." + default = [] +} + +variable "access_tags" { + type = list(string) + description = "A list of access tags to apply to the Databases for MongoDB instance created by the solution. [Learn more](https://cloud.ibm.com/docs/account?topic=account-access-tags-tutorial)." + default = [] +} + +############################################################## +# Encryption +############################################################## + +variable "use_ibm_owned_encryption_key" { + type = bool + description = "IBM Cloud Databases will secure your deployment's data at rest automatically with an encryption key that IBM hold. Alternatively, you may select your own Key Management System instance and encryption key (Key Protect or Hyper Protect Crypto Services) by setting this to false. If setting to false, a value must be passed for `existing_kms_instance_crn` to create a new key, or `existing_kms_key_crn` and/or `existing_backup_kms_key_crn` to use an existing key." + default = false +} + +variable "existing_kms_instance_crn" { + type = string + description = "The CRN of a Key Protect or Hyper Protect Crypto Services instance. Required to create a new encryption key and key ring which will be used to encrypt both deployment data and backups. Applies only if `use_ibm_owned_encryption_key` is false. To use an existing key, pass values for `existing_kms_key_crn` and/or `existing_backup_kms_key_crn`. Bare in mind that backups encryption is only available in certain regions. See [Bring your own key for backups](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok) and [Using the HPCS Key for Backup encryption](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups)." + default = null +} + +variable "existing_kms_key_crn" { + type = string + description = "The CRN of a Key Protect or Hyper Protect Crypto Services encryption key to encrypt your data. Applies only if `use_ibm_owned_encryption_key` is false. By default this key is used for both deployment data and backups, but this behaviour can be altered using the optional `existing_backup_kms_key_crn` input. If no value is passed a new key will be created in the instance specified in the `existing_kms_instance_crn` input. Bare in mind that backups encryption is only available in certain regions. See [Bring your own key for backups](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok) and [Using the HPCS Key for Backup encryption](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups)." + default = null +} + +variable "kms_endpoint_type" { + type = string + description = "The type of endpoint to use for communicating with the Key Protect or Hyper Protect Crypto Services instance. Possible values: `public`, `private`. Applies only if `existing_kms_key_crn` is not specified." + default = "private" + validation { + condition = can(regex("public|private", var.kms_endpoint_type)) + error_message = "The kms_endpoint_type value must be 'public' or 'private'." + } +} + +variable "skip_mongodb_kms_auth_policy" { + type = bool + description = "Whether to create an IAM authorization policy that permits all Databases for MongoDB instances in the resource group to read the encryption key from the Hyper Protect Crypto Services instance specified in the `existing_kms_instance_crn` variable." + default = false +} + +variable "ibmcloud_kms_api_key" { + type = string + description = "The IBM Cloud API key that can create a root key and key ring in the key management service (KMS) instance. If not specified, the 'ibmcloud_api_key' variable is used. Specify this key if the instance in `existing_kms_instance_crn` is in an account that's different from the MongoDB instance. Leave this input empty if the same account owns both instances." + sensitive = true + default = null +} + +variable "key_ring_name" { + type = string + default = "mongodb-key-ring" + description = "The name for the key ring created for the Databases for MongoDB key. Applies only if not specifying an existing key. If a prefix input variable is specified, the prefix is added to the name in the `-` format." +} + +variable "key_name" { + type = string + default = "mongodb-key" + description = "The name for the key created for the Databases for MongoDB key. Applies only if not specifying an existing key. If a prefix input variable is specified, the prefix is added to the name in the `-` format." +} + +variable "existing_backup_kms_key_crn" { + type = string + description = "The CRN of a Key Protect or Hyper Protect Crypto Services encryption key that you want to use for encrypting the disk that holds deployment backups. Applies only if `use_ibm_owned_encryption_key` is false. If no value is passed, the value of `existing_kms_key_crn` is used. If no value is passed for `existing_kms_key_crn`, a new key will be created in the instance specified in the `existing_kms_instance_crn` input. Alternatively set `use_default_backup_encryption_key` to true to use the IBM Cloud Databases default encryption. Bare in mind that backups encryption is only available in certain regions. See [Bring your own key for backups](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok) and [Using the HPCS Key for Backup encryption](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups)." + default = null +} + +variable "use_default_backup_encryption_key" { + type = bool + description = "When `use_ibm_owned_encryption_key` is set to false, backups will be encrypted with either the key specified in `existing_kms_key_crn`, in `existing_backup_kms_key_crn`, or with a new key that will be created in the instance specified in the `existing_kms_instance_crn` input. If you do not want to use your own key for backups encryption, you can set this to `true` to use the IBM Cloud Databases default encryption for backups. Alternatively set `use_ibm_owned_encryption_key` to true to use the default encryption for both backups and deployment data." + default = false +} + +variable "backup_crn" { + type = string + description = "The CRN of a backup resource to restore from. The backup is created by a database deployment with the same service ID. The backup is loaded after provisioning and the new deployment starts up that uses that data. A backup CRN is in the format crn:v1:<…>:backup:. If omitted, the database is provisioned empty." + default = null + + validation { + condition = anytrue([ + var.backup_crn == null, + can(regex("^crn:.*:backup:", var.backup_crn)) + ]) + error_message = "backup_crn must be null OR starts with 'crn:' and contains ':backup:'" + } +} +variable "provider_visibility" { + description = "Set the visibility value for the IBM terraform provider. Supported values are `public`, `private`, `public-and-private`. [Learn more](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/guides/custom-service-endpoints)." + type = string + default = "private" + + validation { + condition = contains(["public", "private", "public-and-private"], var.provider_visibility) + error_message = "Invalid visibility option. Allowed values are 'public', 'private', or 'public-and-private'." + } +} + +############################################################## +# Auto Scaling +############################################################## + +variable "auto_scaling" { + type = object({ + disk = object({ + capacity_enabled = optional(bool, false) + free_space_less_than_percent = optional(number, 10) + io_above_percent = optional(number, 90) + io_enabled = optional(bool, false) + io_over_period = optional(string, "15m") + rate_increase_percent = optional(number, 10) + rate_limit_mb_per_member = optional(number, 3670016) + rate_period_seconds = optional(number, 900) + rate_units = optional(string, "mb") + }) + memory = object({ + io_above_percent = optional(number, 90) + io_enabled = optional(bool, false) + io_over_period = optional(string, "15m") + rate_increase_percent = optional(number, 10) + rate_limit_mb_per_member = optional(number, 114688) + rate_period_seconds = optional(number, 900) + rate_units = optional(string, "mb") + }) + }) + description = "Optional rules to allow the database to increase resources in response to usage. Only a single autoscaling block is allowed. Make sure you understand the effects of autoscaling, especially for production environments. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-icd-mongodb/blob/main/solutions/standard/DA-types.md#autoscaling)" + default = null +} + +############################################################################## +## Secrets Manager Service Credentials +############################################################################## + +variable "existing_secrets_manager_instance_crn" { + type = string + default = null + description = "The CRN of existing secrets manager to use to create service credential secrets for Databases for MongoDB instance." +} + +variable "existing_secrets_manager_endpoint_type" { + type = string + description = "The endpoint type to use if `existing_secrets_manager_instance_crn` is specified. Possible values: public, private." + default = "private" + validation { + condition = contains(["public", "private"], var.existing_secrets_manager_endpoint_type) + error_message = "Only \"public\" and \"private\" are allowed values for 'existing_secrets_endpoint_type'." + } +} + +variable "service_credential_secrets" { + type = list(object({ + secret_group_name = string + secret_group_description = optional(string) + existing_secret_group = optional(bool) + service_credentials = list(object({ + secret_name = string + service_credentials_source_service_role = string + secret_labels = optional(list(string)) + secret_auto_rotation = optional(bool) + secret_auto_rotation_unit = optional(string) + secret_auto_rotation_interval = optional(number) + service_credentials_ttl = optional(string) + service_credential_secret_description = optional(string) + + })) + })) + default = [] + description = "Service credential secrets configuration for Databases for MongoDB. [Learn more](https://github.com/terraform-ibm-modules/terraform-ibm-icd-mongodb/tree/main/solutions/standard/DA-types.md#service-credential-secrets)." + + validation { + condition = alltrue([ + for group in var.service_credential_secrets : alltrue([ + for credential in group.service_credentials : contains( + ["Writer", "Reader", "Manager", "None"], credential.service_credentials_source_service_role + ) + ]) + ]) + error_message = "service_credentials_source_service_role role must be one of 'Writer', 'Reader', 'Manager', and 'None'." + + } +} + +variable "skip_mongodb_sm_auth_policy" { + type = bool + description = "Whether an IAM authorization policy is created for Secrets Manager instance to create a service credential secrets for Databases for MongoDB. If set to false, the Secrets Manager instance passed by the user is granted the Key Manager access to the MongoDB instance created by the Deployable Architecture. Set to `true` to use an existing policy. The value of this is ignored if any value for 'existing_secrets_manager_instance_crn' is not passed." + default = false +} diff --git a/solutions/standard/version.tf b/solutions/standard/version.tf new file mode 100644 index 00000000..672c93d1 --- /dev/null +++ b/solutions/standard/version.tf @@ -0,0 +1,18 @@ +terraform { + required_version = ">= 1.3.0" + # Lock DA into an exact provider version - renovate automation will keep it updated + required_providers { + ibm = { + source = "IBM-Cloud/ibm" + version = "1.74.0" + } + time = { + source = "hashicorp/time" + version = "0.12.1" + } + random = { + source = "hashicorp/random" + version = "3.6.3" + } + } +} diff --git a/tests/other_test.go b/tests/other_test.go index d2da38b8..150a7a1c 100644 --- a/tests/other_test.go +++ b/tests/other_test.go @@ -5,6 +5,7 @@ import ( "fmt" "testing" + "github.com/gruntwork-io/terratest/modules/terraform" "github.com/stretchr/testify/assert" "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testhelper" ) @@ -71,3 +72,37 @@ func TestRunRestoredDBExample(t *testing.T) { assert.Nil(t, err, "This should not have errored") assert.NotNil(t, output, "Expected some output") } + +func TestRunFSCloudExample(t *testing.T) { + t.Parallel() + options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{ + Testing: t, + TerraformDir: "examples/fscloud", + Prefix: "mongodb-fscloud", + Region: "us-south", // For FSCloud locking into us-south since that is where the HPCS permanent instance is + /* + Comment out the 'ResourceGroup' input to force this test to create a unique resource group to ensure tests do + not clash. This is due to the fact that an auth policy may already exist in this resource group since we are + re-using a permanent HPCS instance. By using a new resource group, the auth policy will not already exist + since this module scopes auth policies by resource group. + */ + //ResourceGroup: resourceGroup, + TerraformVars: map[string]interface{}{ + "access_tags": permanentResources["accessTags"], + "kms_key_crn": permanentResources["hpcs_south_root_key_crn"], + "mongodb_version": "6.0", // Always lock this test into the latest supported MongoDB version + }, + CloudInfoService: sharedInfoSvc, + }) + options.SkipTestTearDown = true + output, err := options.RunTestConsistency() + assert.Nil(t, err, "This should not have errored") + assert.NotNil(t, output, "Expected some output") + + // check if outputs exist + outputs := terraform.OutputAll(options.Testing, options.TerraformOptions) + expectedOutputs := []string{"port", "hostname"} + _, outputErr := testhelper.ValidateTerraformOutputs(outputs, expectedOutputs...) + assert.NoErrorf(t, outputErr, "Some outputs not found or nil") + options.TestTearDown() +} diff --git a/tests/pr_test.go b/tests/pr_test.go index 0b9a1f1a..0578ee87 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -4,17 +4,25 @@ package test import ( "crypto/rand" "encoding/base64" + "encoding/json" + "fmt" + "io/fs" "log" "os" + "path/filepath" + "strings" "testing" - "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/cloudinfo" "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/common" "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testhelper" + "github.com/terraform-ibm-modules/ibmcloud-terratest-wrapper/testschematic" ) +const standardSolutionTerraformDir = "solutions/standard" + // Use existing resource group const resourceGroup = "geretain-test-mongo" @@ -41,39 +49,151 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func TestRunFSCloudExample(t *testing.T) { +type tarIncludePatterns struct { + excludeDirs []string + + includeFiletypes []string + + includeDirs []string +} + +func getTarIncludePatternsRecursively(dir string, dirsToExclude []string, fileTypesToInclude []string) ([]string, error) { + r := tarIncludePatterns{dirsToExclude, fileTypesToInclude, nil} + err := filepath.WalkDir(dir, func(path string, entry fs.DirEntry, err error) error { + return walk(&r, path, entry, err) + }) + if err != nil { + fmt.Println("error") + return r.includeDirs, err + } + return r.includeDirs, nil +} + +func walk(r *tarIncludePatterns, s string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + for _, excludeDir := range r.excludeDirs { + if strings.Contains(s, excludeDir) { + return nil + } + } + if s == ".." { + r.includeDirs = append(r.includeDirs, "*.tf") + return nil + } + for _, includeFiletype := range r.includeFiletypes { + r.includeDirs = append(r.includeDirs, strings.ReplaceAll(s+"/*"+includeFiletype, "../", "")) + } + } + return nil +} + +func TestRunStandardSolutionSchematics(t *testing.T) { t.Parallel() - options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{ - Testing: t, - TerraformDir: "examples/fscloud", - Prefix: "mongodb-fscloud", - Region: "us-south", // For FSCloud locking into us-south since that is where the HPCS permanent instance is - /* - Comment out the 'ResourceGroup' input to force this test to create a unique resource group to ensure tests do - not clash. This is due to the fact that an auth policy may already exist in this resource group since we are - re-using a permanent HPCS instance. By using a new resource group, the auth policy will not already exist - since this module scopes auth policies by resource group. - */ - //ResourceGroup: resourceGroup, - TerraformVars: map[string]interface{}{ - "access_tags": permanentResources["accessTags"], - "existing_kms_instance_guid": permanentResources["hpcs_south"], - "kms_key_crn": permanentResources["hpcs_south_root_key_crn"], - "mongodb_version": "6.0", // Always lock this test into the latest supported MongoDB version - }, - CloudInfoService: sharedInfoSvc, + + excludeDirs := []string{ + ".terraform", + ".docs", + ".github", + ".git", + ".idea", + "common-dev-assets", + "examples", + "tests", + "reference-architectures", + } + includeFiletypes := []string{ + ".tf", + ".yaml", + ".py", + ".tpl", + ".sh", + } + + tarIncludePatterns, recurseErr := getTarIncludePatternsRecursively("..", excludeDirs, includeFiletypes) + + // if error producing tar patterns (very unexpected) fail test immediately + require.NoError(t, recurseErr, "Schematic Test had unexpected error traversing directory tree") + prefix := "mongodb-st-da" + options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ + Testing: t, + TarIncludePatterns: tarIncludePatterns, + TemplateFolder: standardSolutionTerraformDir, + BestRegionYAMLPath: regionSelectionPath, + Prefix: prefix, + ResourceGroup: resourceGroup, + DeleteWorkspaceOnFail: false, + WaitJobCompleteMinutes: 60, }) - options.SkipTestTearDown = true - output, err := options.RunTestConsistency() + + serviceCredentialSecrets := []map[string]interface{}{ + { + "secret_group_name": fmt.Sprintf("%s-secret-group", options.Prefix), + "service_credentials": []map[string]string{ + { + "secret_name": fmt.Sprintf("%s-cred-reader", options.Prefix), + "service_credentials_source_service_role": "Reader", + }, + { + "secret_name": fmt.Sprintf("%s-cred-writer", options.Prefix), + "service_credentials_source_service_role": "Writer", + }, + }, + }, + } + + serviceCredentialNames := map[string]string{ + "admin": "Administrator", + "user1": "Viewer", + "user2": "Editor", + } + + serviceCredentialNamesJSON, err := json.Marshal(serviceCredentialNames) + if err != nil { + log.Fatalf("Error converting to JSON: %s", err) + } + + options.TerraformVars = []testschematic.TestSchematicTerraformVar{ + {Name: "ibmcloud_api_key", Value: options.RequiredEnvironmentVars["TF_VAR_ibmcloud_api_key"], DataType: "string", Secure: true}, + {Name: "access_tags", Value: permanentResources["accessTags"], DataType: "list(string)"}, + {Name: "existing_kms_instance_crn", Value: permanentResources["hpcs_south_crn"], DataType: "string"}, + {Name: "kms_endpoint_type", Value: "private", DataType: "string"}, + {Name: "mongodb_version", Value: "6.0", DataType: "string"}, // Always lock this test into the latest supported MongoDB version + {Name: "resource_group_name", Value: options.Prefix, DataType: "string"}, + {Name: "existing_secrets_manager_instance_crn", Value: permanentResources["secretsManagerCRN"], DataType: "string"}, + {Name: "service_credential_secrets", Value: serviceCredentialSecrets, DataType: "list(object)"}, + {Name: "service_credential_names", Value: string(serviceCredentialNamesJSON), DataType: "map(string)"}, + } + err = options.RunSchematicTest() assert.Nil(t, err, "This should not have errored") - assert.NotNil(t, output, "Expected some output") - - // check if outputs exist - outputs := terraform.OutputAll(options.Testing, options.TerraformOptions) - expectedOutputs := []string{"port", "hostname"} - _, outputErr := testhelper.ValidateTerraformOutputs(outputs, expectedOutputs...) - assert.NoErrorf(t, outputErr, "Some outputs not found or nil") - options.TestTearDown() +} + +func TestRunStandardUpgradeSolution(t *testing.T) { + t.Parallel() + + options := testhelper.TestOptionsDefault(&testhelper.TestOptions{ + Testing: t, + TerraformDir: standardSolutionTerraformDir, + BestRegionYAMLPath: regionSelectionPath, + Prefix: "mongodb-st-da-upg", + ResourceGroup: resourceGroup, + }) + + options.TerraformVars = map[string]interface{}{ + "access_tags": permanentResources["accessTags"], + "existing_kms_instance_crn": permanentResources["hpcs_south_crn"], + "kms_endpoint_type": "public", + "provider_visibility": "public", + "resource_group_name": options.Prefix, + } + + output, err := options.RunTestUpgrade() + if !options.UpgradeTestSkipped { + assert.Nil(t, err, "This should not have errored") + assert.NotNil(t, output, "Expected some output") + } } func TestRunCompleteUpgradeExample(t *testing.T) { diff --git a/variables.tf b/variables.tf index 6fa7ea32..f49b864e 100644 --- a/variables.tf +++ b/variables.tf @@ -180,21 +180,21 @@ variable "auto_scaling" { # Encryption ############################################################## -variable "kms_encryption_enabled" { +variable "use_ibm_owned_encryption_key" { type = bool - description = "Set this to true to control the encryption keys used to encrypt the data that you store in IBM Cloud® Databases. If set to false, the data is encrypted by using randomly generated keys. For more info on Key Protect integration, see https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect. For more info on HPCS integration, see https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs" - default = false + description = "IBM Cloud Databases will secure your deployment's data at rest automatically with an encryption key that IBM hold. Alternatively, you may select your own Key Management System instance and encryption key (Key Protect or Hyper Protect Crypto Services) by setting this to false. If setting to false, a value must be passed for the `kms_key_crn` input." + default = true } variable "use_default_backup_encryption_key" { type = bool - description = "Set to true to use default ICD randomly generated keys." + description = "When `use_ibm_owned_encryption_key` is set to false, backups will be encrypted with either the key specified in `kms_key_crn`, or in `backup_encryption_key_crn` if a value is passed. If you do not want to use your own key for backups encryption, you can set this to `true` to use the IBM Cloud Databases default encryption for backups. Alternatively set `use_ibm_owned_encryption_key` to true to use the default encryption for both backups and deployment data." default = false } variable "kms_key_crn" { type = string - description = "The root key CRN of a Key Management Services like Key Protect or Hyper Protect Crypto Services (HPCS) that you want to use for disk encryption. Only used if var.kms_encryption_enabled is set to true." + description = "The CRN of a Key Protect or Hyper Protect Crypto Services encryption key to encrypt your data. Applies only if `use_ibm_owned_encryption_key` is false. By default this key is used for both deployment data and backups, but this behaviour can be altered using the `use_same_kms_key_for_backups` and `backup_encryption_key_crn` inputs. Bare in mind that backups encryption is only available in certain regions. See [Bring your own key for backups](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok) and [Using the HPCS Key for Backup encryption](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups)." default = null validation { @@ -203,33 +203,37 @@ variable "kms_key_crn" { can(regex(".*kms.*", var.kms_key_crn)), can(regex(".*hs-crypto.*", var.kms_key_crn)), ]) - error_message = "Value must be the root key CRN from either the Key Protect or Hyper Protect Crypto Services (HPCS)" + error_message = "Value must be the KMS key CRN from a Key Protect or Hyper Protect Crypto Services instance." } } +variable "use_same_kms_key_for_backups" { + type = bool + description = "Set this to false if you wan't to use a different key that you own to encrypt backups. When set to false, a value is required for the `backup_encryption_key_crn` input. Alternatiely set `use_default_backup_encryption_key` to true to use the IBM Cloud Databases default encryption. Applies only if `use_ibm_owned_encryption_key` is false. Bare in mind that backups encryption is only available in certain regions. See [Bring your own key for backups](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok) and [Using the HPCS Key for Backup encryption](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups)." + default = true +} + variable "backup_encryption_key_crn" { type = string - description = "The CRN of a KMS (Key Protect or Hyper Protect Crypto Services) key to use for encrypting the disk that holds deployment backups. Only used if var.kms_encryption_enabled is set to true. There are limitation per region on the type of KMS service (Key Protect or Hyper Protect Crypto Services) and region for those services. See https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok and https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups" + description = "The CRN of a Key Protect or Hyper Protect Crypto Services encryption key that you want to use for encrypting the disk that holds deployment backups. Applies only if `use_ibm_owned_encryption_key` is false and `use_same_kms_key_for_backups` is false. If no value is passed, and `use_same_kms_key_for_backups` is true, the value of `kms_key_crn` is used. Alternatively set `use_default_backup_encryption_key` to true to use the IBM Cloud Databases default encryption. Bare in mind that backups encryption is only available in certain regions. See [Bring your own key for backups](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok) and [Using the HPCS Key for Backup encryption](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups)." default = null validation { - condition = var.backup_encryption_key_crn == null ? true : length(regexall("^crn:v1:bluemix:public:kms:(us-south|us-east|eu-de):a/[[:xdigit:]]{32}:[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}:key:[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}$|^crn:v1:bluemix:public:hs-crypto:[a-z-]+:a/[[:xdigit:]]{32}:[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}:key:[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}$", var.backup_encryption_key_crn)) > 0 - error_message = "Valid values for backup_encryption_key_crn is null, a Hyper Protect Crypto Services key CRN or a Key Protect key CRN from us-south, us-east or eu-de" + condition = anytrue([ + var.backup_encryption_key_crn == null, + can(regex(".*kms.*", var.backup_encryption_key_crn)), + can(regex(".*hs-crypto.*", var.backup_encryption_key_crn)), + ]) + error_message = "Value must be the KMS key CRN from a Key Protect or Hyper Protect Crypto Services instance in one of the supported backup regions." } } variable "skip_iam_authorization_policy" { type = bool - description = "Set to true to skip the creation of an IAM authorization policy that permits all MongoDB database instances in the resource group to read the encryption key from the KMS instance. If set to false, pass in a value for the KMS instance in the existing_kms_instance_guid variable. In addition, no policy is created if var.kms_encryption_enabled is set to false." + description = "Set to true to skip the creation of IAM authorization policies that permits all Databases for MongoDB instances in the given resource group 'Reader' access to the Key Protect or Hyper Protect Crypto Services key that was provided in the `kms_key_crn` and `backup_encryption_key_crn` inputs. This policy is required in order to enable KMS encryption, so only skip creation if there is one already present in your account. No policy is created if `use_ibm_owned_encryption_key` is true." default = false } -variable "existing_kms_instance_guid" { - type = string - description = "The GUID of the Hyper Protect Crypto Services or Key Protect instance in which the key specified in var.kms_key_crn and var.backup_encryption_key_crn is coming from. Required only if var.kms_encryption_enabled is set to true, var.skip_iam_authorization_policy is set to false, and you pass a value for var.kms_key_crn, var.backup_encryption_key_crn, or both." - default = null -} - ############################################################## # Context-based restriction (CBR) ##############################################################