diff --git a/README.md b/README.md index 19761c39..d2c6e24d 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ 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, the admin user ID cannot be accessed. You can specify more users in a user block. | `string` | `null` | no | | [auto\_scaling](#input\_auto\_scaling) | The 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://cloud.ibm.com/docs/databases-for-elasticsearch?topic=databases-for-elasticsearch-autoscaling&interface=cli#autoscaling-considerations). |
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 both provisioning is complete and the new deployment that uses that data starts. Specify a backup CRN is in the format `crn:v1:<...>:backup:`. If not specified, 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. Applies only if `kms_encryption_enabled` is true. Limitations exist for regions. For more information, see [Key Protect integration](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok) or [Hyper Protect Crypto Services integration](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 Hyper Protect Crypto Services use for encrypting the disk that holds deployment backups. 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 | | [cbr\_rules](#input\_cbr\_rules) | The list of context-based restriction 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 | | [elasticsearch\_version](#input\_elasticsearch\_version) | The version of Databases for Elasticsearch to deploy. Possible values: `8.7`, `8.10`, `8.12`, which requires an Enterprise Platinum pricing plan. If no value is specified, the current preferred version for IBM Cloud Databases is used. | `string` | `null` | no | | [elser\_model\_type](#input\_elser\_model\_type) | Trained ELSER model to be used for Elastic's Natural Language Processing. Possible values: `.elser_model_1`, `.elser_model_2` and `.elser_model_2_linux-x86_64`. [Learn more](https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-elser.html) | `string` | `".elser_model_2_linux-x86_64"` | no | @@ -112,7 +112,7 @@ You need the following permissions to run this module. | [service\_endpoints](#input\_service\_endpoints) | The type of endpoint of the database instance. Possible values: `public`, `private`, `public-and-private`. | `string` | `"public"` | no | | [skip\_iam\_authorization\_policy](#input\_skip\_iam\_authorization\_policy) | Whether to create an IAM authorization policy that permits all Databases for Elasticsearch instances in the resource group to read the encryption key from the Hyper Protect Crypto Services instance specified in the `existing_kms_instance_guid` variable. If set to `false`, specify a value for the KMS instance in the `existing_kms_instance_guid` variable. No policy is created if `kms_encryption_enabled` is false. | `bool` | `false` | no | | [tags](#input\_tags) | The list of tags to be added to the Databases for Elasticsearch instance. | `list(string)` | `[]` | no | -| [use\_default\_backup\_encryption\_key](#input\_use\_default\_backup\_encryption\_key) | Whether to use the IBM Cloud Databases generated keys. | `bool` | `false` | no | +| [use\_default\_backup\_encryption\_key](#input\_use\_default\_backup\_encryption\_key) | Whether to use the IBM Cloud Databases generated keys for backup encryption. | `bool` | `false` | no | | [users](#input\_users) | The list of users that have access to the database. Multiple blocks are allowed. The user password must be 10-32 characters. In most cases, you can use IAM service credentials (by specifying `service_credential_names`) to control access to the database instance. This block creates native database users. [Learn more](https://cloud.ibm.com/docs/databases-for-elasticsearch?topic=databases-for-elasticsearch-user-management&interface=ui). |
list(object({
name = string
password = string # pragma: allowlist secret
type = optional(string)
role = optional(string)
}))
| `[]` | no | ### Outputs diff --git a/cra-config.yaml b/cra-config.yaml index 59e6a406..9bb47d31 100644 --- a/cra-config.yaml +++ b/cra-config.yaml @@ -8,3 +8,4 @@ CRA_TARGETS: 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_provider_visibility: "public" diff --git a/examples/fscloud/variables.tf b/examples/fscloud/variables.tf index 87df8b45..664994c7 100644 --- a/examples/fscloud/variables.tf +++ b/examples/fscloud/variables.tf @@ -104,7 +104,7 @@ variable "backup_crn" { 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 Hyper Protect Crypto Services use for encrypting the disk that holds deployment backups. 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" default = null # Validation happens in the root module } diff --git a/ibm_catalog.json b/ibm_catalog.json index bf394321..2f116cb4 100644 --- a/ibm_catalog.json +++ b/ibm_catalog.json @@ -89,6 +89,23 @@ { "key": "ibmcloud_api_key" }, + { + "key": "provider_visibility", + "options": [ + { + "displayname": "private", + "value": "private" + }, + { + "displayname": "public", + "value": "public" + }, + { + "displayname": "public-and-private", + "value": "public-and-private" + } + ] + }, { "key": "use_existing_resource_group" }, @@ -186,6 +203,9 @@ { "key": "access_tags" }, + { + "key": "use_ibm_owned_encryption_key" + }, { "key": "tags" }, diff --git a/main.tf b/main.tf index 4384ac4b..e13319f4 100644 --- a/main.tf +++ b/main.tf @@ -25,24 +25,52 @@ locals { # Determine if host_flavor is used host_flavor_set = var.member_host_flavor != null ? true : false - # 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" - create_kp_auth_policy = var.kms_encryption_enabled == false || var.skip_iam_authorization_policy ? 0 : 1 + + parsed_kms_key_crn = var.kms_key_crn != null ? split(":", var.kms_key_crn) : [] + kms_service = length(local.parsed_kms_key_crn) > 0 ? local.parsed_kms_key_crn[4] : null + kms_scope = length(local.parsed_kms_key_crn) > 0 ? local.parsed_kms_key_crn[6] : null + kms_account_id = length(local.parsed_kms_key_crn) > 0 ? split("/", local.kms_scope)[1] : null + kms_key_id = length(local.parsed_kms_key_crn) > 0 ? local.parsed_kms_key_crn[9] : null } # Create IAM Access Policy to allow Key protect to access Elasticsearch instance resource "ibm_iam_authorization_policy" "policy" { - count = local.create_kp_auth_policy - source_service_name = "databases-for-elasticsearch" - 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"] + count = local.create_kp_auth_policy + source_service_name = "databases-for-elasticsearch" + source_resource_group_id = var.resource_group_id + roles = ["Reader"] + description = "Allow all Elastic Search instances in the resource group ${var.resource_group_id} to read the ${local.kms_service} key ${local.kms_key_id} from the instance GUID ${var.existing_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 = var.existing_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 @@ -83,9 +111,9 @@ resource "ibm_database" "elasticsearch" { ## This is used to conditionally add one, OR, the other group block depending on var.local.host_flavor_set ## This block is for if host_flavor IS set to specific pre-defined host sizes and not set to "multitenant" dynamic "group" { - for_each = local.host_flavor_set && var.member_host_flavor != "multitenant" ? [1] : [] + for_each = local.host_flavor_set && var.member_host_flavor != "multitenant" && var.backup_crn == null ? [1] : [] content { - group_id = "member" # Only member type is allowed for postgresql + group_id = "member" # Only member type is allowed for elasticsearch host_flavor { id = var.member_host_flavor } @@ -100,9 +128,9 @@ resource "ibm_database" "elasticsearch" { ## This block is for if host_flavor IS set to "multitenant" dynamic "group" { - for_each = local.host_flavor_set && var.member_host_flavor == "multitenant" ? [1] : [] + for_each = local.host_flavor_set && var.member_host_flavor == "multitenant" && var.backup_crn == null ? [1] : [] content { - group_id = "member" # Only member type is allowed for postgresql + group_id = "member" # Only member type is allowed for elasticsearch host_flavor { id = var.member_host_flavor } @@ -123,9 +151,9 @@ resource "ibm_database" "elasticsearch" { ## This block is for if host_flavor IS NOT set dynamic "group" { - for_each = local.host_flavor_set ? [] : [1] + for_each = local.host_flavor_set == false && var.backup_crn == null ? [1] : [] content { - group_id = "member" # Only member type is allowed for postgresql + group_id = "member" # Only member type is allowed for elasticsearch memory { allocation_mb = var.member_memory_mb } @@ -180,6 +208,8 @@ resource "ibm_database" "elasticsearch" { timeouts { create = "120m" #Extending provisioning time to 120 minutes + update = "120m" + delete = "15m" } } diff --git a/modules/fscloud/README.md b/modules/fscloud/README.md index 38ea0a53..86876a12 100644 --- a/modules/fscloud/README.md +++ b/modules/fscloud/README.md @@ -34,13 +34,13 @@ 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-elasticsearch?topic=databases-for-elasticsearch-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 Hyper Protect Crypto Services (HPCS) or Key Protect root key CRN to use for encrypting the disk that holds deployment backups. There are region limitations for backup encryption. See https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups (HPCS) and https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok (Key Protect). | `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 | | [elasticsearch\_version](#input\_elasticsearch\_version) | Version of the Elasticsearch instance. If no value is passed, the current preferred version of IBM Cloud Databases is used. | `string` | `null` | no | | [elser\_model\_type](#input\_elser\_model\_type) | Trained ELSER model to be used for Elastic's Natural Language Processing. Possible values: `.elser_model_1`, `.elser_model_2` and `.elser_model_2_linux-x86_64`. [Learn more](https://www.elastic.co/guide/en/machine-learning/current/ml-nlp-elser.html) | `string` | `".elser_model_2_linux-x86_64"` | no | | [enable\_elser\_model](#input\_enable\_elser\_model) | Set it to true to install and start the Elastic's Natural Language Processing model. [Learn more](https://cloud.ibm.com/docs/databases-for-elasticsearch?topic=databases-for-elasticsearch-elser-embeddings-elasticsearch) | `bool` | `false` | no | -| [existing\_kms\_instance\_guid](#input\_existing\_kms\_instance\_guid) | The GUID of the Hyper Protect Crypto Services instance. It is only required while creating authorization policy. | `string` | `null` | no | -| [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 | +| [existing\_kms\_instance\_guid](#input\_existing\_kms\_instance\_guid) | The GUID of the Hyper Protect Crypto Services (HPCS) or Key Protect instance. | `string` | `null` | no | +| [kms\_key\_crn](#input\_kms\_key\_crn) | The Hyper Protect Crypto Services (HPCS) or Key Protect root key CRN to use for disk encryption. | `string` | `null` | no | | [member\_cpu\_count](#input\_member\_cpu\_count) | Allocated dedicated CPU per member. For shared CPU, set to 0. [Learn more](https://cloud.ibm.com/docs/databases-for-elasticsearch?topic=databases-for-elasticsearch-resources-scaling) | `number` | `0` | no | | [member\_disk\_mb](#input\_member\_disk\_mb) | Allocated disk per-member. [Learn more}(https://cloud.ibm.com/docs/databases-for-elasticsearch?topic=databases-for-elasticsearch-resources-scaling) | `number` | `5120` | 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 | @@ -51,8 +51,9 @@ 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 Elasticsearch 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 Elasticsearch 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 an IAM authorization policy that permits all ElasticSearch database instances in the resource group to read the encryption key from the Hyper Protect Crypto Services or Key Protect instance. The instance is passed in through the var.existing\_kms\_instance\_guid variable. | `bool` | `false` | no | | [tags](#input\_tags) | Optional list of tags to be added to the Elasticsearch instance. | `list(any)` | `[]` | 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. | `bool` | `false` | 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 Elasticsearch instance. This blocks creates native Elasticsearch database users, more info on that can be found here https://cloud.ibm.com/docs/databases-for-elasticsearch?topic=databases-for-elasticsearch-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 e301a2a3..a40ceb98 100644 --- a/modules/fscloud/main.tf +++ b/modules/fscloud/main.tf @@ -1,3 +1,10 @@ +locals { + # tflint-ignore: terraform_unused_declarations + validate_kms_inputs = !var.use_ibm_owned_encryption_key && (var.kms_key_crn == null || var.existing_kms_instance_guid == null) ? tobool("Values for 'kms_key_crn' and 'existing_kms_instance_guid' must be passed if 'use_ibm_owned_encryption_key' it set to false.") : true + # tflint-ignore: terraform_unused_declarations + validate_kms_inputs_2 = var.use_ibm_owned_encryption_key && (var.kms_key_crn != null || var.backup_encryption_key_crn != null || var.existing_kms_instance_guid != null) ? tobool("'use_ibm_owned_encryption_key' is set to true, but values have been passed for either 'kms_key_crn', 'backup_encryption_key_crn' and/or 'existing_kms_instance_guid'. To use BYOK or KYOK encryption, ensure to set 'use_ibm_owned_encryption_key' to false, and pass values for 'kms_key_crn', 'backup_encryption_key_crn' (optional) and 'existing_kms_instance_guid'. Alternatively do not pass any values for 'kms_key_crn', 'backup_encryption_key_crn' and 'existing_kms_instance_guid' to use the IBM owned encryption keys.") : true +} + module "elasticsearch" { source = "../../" resource_group_id = var.resource_group_id @@ -6,7 +13,7 @@ module "elasticsearch" { skip_iam_authorization_policy = var.skip_iam_authorization_policy service_endpoints = "private" elasticsearch_version = var.elasticsearch_version - kms_encryption_enabled = true + kms_encryption_enabled = !var.use_ibm_owned_encryption_key existing_kms_instance_guid = var.existing_kms_instance_guid kms_key_crn = var.kms_key_crn backup_crn = var.backup_crn diff --git a/modules/fscloud/variables.tf b/modules/fscloud/variables.tf index 214d70e1..d6d0bc3c 100644 --- a/modules/fscloud/variables.tf +++ b/modules/fscloud/variables.tf @@ -136,26 +136,33 @@ variable "auto_scaling" { # Encryption ############################################################## +variable "use_ibm_owned_encryption_key" { + type = bool + 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 Hyper Protect Crypto Services (HPCS) or Key Protect root key CRN to use for disk encryption." + default = null } 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 Hyper Protect Crypto Services (HPCS) or Key Protect root key CRN to use for encrypting the disk that holds deployment backups. There are region limitations for backup encryption. See https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups (HPCS) and https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok (Key Protect)." default = null } variable "skip_iam_authorization_policy" { type = bool - description = "Set to true to skip the creation of an IAM authorization policy that permits all Elasticsearch 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 = "Set to true to skip the creation of an IAM authorization policy that permits all ElasticSearch database instances in the resource group to read the encryption key from the Hyper Protect Crypto Services or Key Protect instance. The instance is passed in through the var.existing_kms_instance_guid variable." default = false } variable "existing_kms_instance_guid" { type = string - description = "The GUID of the Hyper Protect Crypto Services instance. It is only required while creating authorization policy." + description = "The GUID of the Hyper Protect Crypto Services (HPCS) or Key Protect instance." default = null } diff --git a/solutions/standard/main.tf b/solutions/standard/main.tf index 531ba950..d09e2b3f 100644 --- a/solutions/standard/main.tf +++ b/solutions/standard/main.tf @@ -11,16 +11,18 @@ locals { elasticsearch_key_ring_name = var.prefix != null ? "${var.prefix}-${var.elasticsearch_key_ring_name}" : var.elasticsearch_key_ring_name - kms_key_crn = var.existing_db_instance_crn != null ? null : (var.existing_kms_key_crn != null ? var.existing_kms_key_crn : module.kms[0].keys[format("%s.%s", local.elasticsearch_key_ring_name, local.elasticsearch_key_name)].crn) - existing_db_instance_guid = var.existing_db_instance_crn != null ? element(split(":", var.existing_db_instance_crn), length(split(":", var.existing_db_instance_crn)) - 3) : null use_existing_db_instance = var.existing_db_instance_crn != null - create_cross_account_auth_policy = !var.skip_iam_authorization_policy && var.ibmcloud_kms_api_key != null + create_cross_account_auth_policy = !var.skip_iam_authorization_policy && var.ibmcloud_kms_api_key != null && !var.use_ibm_owned_encryption_key create_sm_auth_policy = var.skip_es_sm_auth_policy || var.existing_secrets_manager_instance_crn == null ? 0 : 1 - kms_service_name = local.kms_key_crn != null ? ( - can(regex(".*kms.*", local.kms_key_crn)) ? "kms" : can(regex(".*hs-crypto.*", local.kms_key_crn)) ? "hs-crypto" : null - ) : null + + kms_key_crn = var.existing_db_instance_crn != null ? null : !var.use_ibm_owned_encryption_key ? var.existing_kms_key_crn != null ? var.existing_kms_key_crn : module.kms[0].keys[format("%s.%s", local.elasticsearch_key_ring_name, local.elasticsearch_key_name)].crn : null + parsed_kms_key_crn = local.kms_key_crn != null ? split(":", local.kms_key_crn) : [] + kms_service = length(local.parsed_kms_key_crn) > 0 ? local.parsed_kms_key_crn[4] : null + kms_scope = length(local.parsed_kms_key_crn) > 0 ? local.parsed_kms_key_crn[6] : null + kms_account_id = length(local.parsed_kms_key_crn) > 0 ? split("/", local.kms_scope)[1] : null + kms_key_id = length(local.parsed_kms_key_crn) > 0 ? local.parsed_kms_key_crn[9] : null elasticsearch_guid = local.use_existing_db_instance ? data.ibm_database.existing_db_instance[0].guid : module.elasticsearch[0].guid } @@ -45,15 +47,43 @@ data "ibm_iam_account_settings" "iam_account_settings" { } resource "ibm_iam_authorization_policy" "kms_policy" { - count = local.create_cross_account_auth_policy ? 1 : 0 - provider = ibm.kms - source_service_account = data.ibm_iam_account_settings.iam_account_settings[0].account_id - source_service_name = "databases-for-elasticsearch" - source_resource_group_id = module.resource_group.resource_group_id - target_service_name = local.kms_service_name - target_resource_instance_id = local.existing_kms_instance_guid - roles = ["Reader"] - description = "Allow all Elastic Search instances in the resource group ${module.resource_group.resource_group_id} in the account ${data.ibm_iam_account_settings.iam_account_settings[0].account_id} to read from the ${local.kms_service_name} instance GUID ${local.existing_kms_instance_guid}" + count = local.create_cross_account_auth_policy ? 1 : 0 + provider = ibm.kms + source_service_account = data.ibm_iam_account_settings.iam_account_settings[0].account_id + source_service_name = "databases-for-elasticsearch" + source_resource_group_id = module.resource_group.resource_group_id + roles = ["Reader"] + description = "Allow all Elastic Search instances in the resource group ${module.resource_group.resource_group_id} in the account ${data.ibm_iam_account_settings.iam_account_settings[0].account_id} to read the ${local.kms_service} key ${local.kms_key_id} from the instance GUID ${local.existing_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.existing_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 @@ -67,7 +97,7 @@ module "kms" { providers = { ibm = ibm.kms } - count = var.existing_kms_key_crn != null || local.use_existing_db_instance ? 0 : 1 # no need to create any KMS resources if passing an existing key + count = var.existing_kms_key_crn != null || local.use_existing_db_instance || var.use_ibm_owned_encryption_key ? 0 : 1 # no need to create any KMS resources if passing an existing key or using IBM owned keys source = "terraform-ibm-modules/kms-all-inclusive/ibm" version = "4.16.4" create_key_protect_instance = false @@ -107,6 +137,7 @@ module "elasticsearch" { skip_iam_authorization_policy = var.skip_iam_authorization_policy || local.create_cross_account_auth_policy elasticsearch_version = var.elasticsearch_version existing_kms_instance_guid = local.existing_kms_instance_guid + use_ibm_owned_encryption_key = var.use_ibm_owned_encryption_key kms_key_crn = local.kms_key_crn access_tags = var.access_tags tags = var.tags diff --git a/solutions/standard/provider.tf b/solutions/standard/provider.tf index c2a60b2a..91e435f9 100644 --- a/solutions/standard/provider.tf +++ b/solutions/standard/provider.tf @@ -1,10 +1,12 @@ 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.existing_kms_instance_region + visibility = var.provider_visibility } diff --git a/solutions/standard/variables.tf b/solutions/standard/variables.tf index 5bdc24ff..b378fcbb 100644 --- a/solutions/standard/variables.tf +++ b/solutions/standard/variables.tf @@ -15,6 +15,17 @@ variable "ibmcloud_kms_api_key" { default = null } +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'." + } +} + variable "prefix" { type = string description = "Prefix to add to all resources created by this solution." @@ -230,13 +241,19 @@ variable "existing_kms_instance_crn" { variable "elasticsearch_key_ring_name" { type = string default = "elasticsearch-key-ring" - description = "The name for the key ring created for the Databases for Elasticsearch 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." + description = "The name for the key ring created for the ElasticSearch key. Applies only if not specifying an existing key or using IBM owned keys. If a prefix input variable is specified, the prefix is added to the name in the `-` format." +} + +variable "use_ibm_owned_encryption_key" { + type = bool + description = "Set to true to use the default IBM Cloud® Databases randomly generated keys for disk and backups encryption." + default = false } variable "elasticsearch_key_name" { type = string default = "elasticsearch-key" - description = "The name for the key created for the Databases for Elasticsearch 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." + description = "The name for the key created for the ElasticSearch key. Applies only if not specifying an existing key or using IBM owned keys. If a prefix input variable is specified, the prefix is added to the name in the `-` format." } ############################################################################## diff --git a/tests/pr_test.go b/tests/pr_test.go index 63fa38c6..2808e82a 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -151,13 +151,12 @@ func TestRunStandardSolutionSchematics(t *testing.T) { // if error producing tar patterns (very unexpected) fail test immediately require.NoError(t, recurseErr, "Schematic Test had unexpected error traversing directory tree") - prefix := "els-sr-da" options := testschematic.TestSchematicOptionsDefault(&testschematic.TestSchematicOptions{ Testing: t, TarIncludePatterns: tarIncludePatterns, TemplateFolder: standardSolutionTerraformDir, BestRegionYAMLPath: regionSelectionPath, - Prefix: prefix, + Prefix: "els-sr-da", ResourceGroup: resourceGroup, DeleteWorkspaceOnFail: false, WaitJobCompleteMinutes: 60, @@ -165,14 +164,14 @@ func TestRunStandardSolutionSchematics(t *testing.T) { serviceCredentialSecrets := []map[string]interface{}{ { - "secret_group_name": fmt.Sprintf("%s-secret-group", prefix), + "secret_group_name": fmt.Sprintf("%s-secret-group", options.Prefix), "service_credentials": []map[string]string{ { - "secret_name": fmt.Sprintf("%s-cred-reader", prefix), + "secret_name": fmt.Sprintf("%s-cred-reader", options.Prefix), "service_credentials_source_service_role": "Reader", }, { - "secret_name": fmt.Sprintf("%s-cred-writer", prefix), + "secret_name": fmt.Sprintf("%s-cred-writer", options.Prefix), "service_credentials_source_service_role": "Writer", }, }, @@ -193,6 +192,8 @@ func TestRunStandardSolutionSchematics(t *testing.T) { {Name: "admin_pass_sm_secret_group", Value: options.Prefix, DataType: "string"}, {Name: "admin_pass_sm_secret_name", Value: options.Prefix, DataType: "string"}, {Name: "enable_kibana_dashboard", Value: true, DataType: "bool"}, + {Name: "provider_visibility", Value: "private", DataType: "string"}, + {Name: "prefix", Value: options.Prefix, DataType: "string"}, } err := options.RunSchematicTest() assert.Nil(t, err, "This should not have errored") @@ -214,6 +215,7 @@ func TestRunStandardUpgradeSolution(t *testing.T) { "existing_kms_instance_crn": permanentResources["hpcs_south_crn"], "kms_endpoint_type": "public", "resource_group_name": options.Prefix, + "provider_visibility": "public", // Currently, we can not have upgrade test for elser model, because test provision private endpoint for ES (fscloud profile), and script can not connect to private ES API without schematics // "plan": "platinum", // "enable_elser_model": true, @@ -246,3 +248,27 @@ func TestRunBasicExample(t *testing.T) { assert.Nil(t, err, "This should not have errored") assert.NotNil(t, output, "Expected some output") } + +// Test the DA when using IBM owned encryption keys +func TestRunStandardSolutionIBMKeys(t *testing.T) { + t.Parallel() + + options := testhelper.TestOptionsDefault(&testhelper.TestOptions{ + Testing: t, + TerraformDir: standardSolutionTerraformDir, + Region: "us-south", + Prefix: "es-icd-key", + ResourceGroup: resourceGroup, + }) + + options.TerraformVars = map[string]interface{}{ + "elasticsearch_version": "8.12", + "provider_visibility": "public", + "resource_group_name": options.Prefix, + "use_ibm_owned_encryption_key": true, + } + + output, err := options.RunTestConsistency() + assert.Nil(t, err, "This should not have errored") + assert.NotNil(t, output, "Expected some output") +} diff --git a/variables.tf b/variables.tf index 329a293b..dcdea5e2 100644 --- a/variables.tf +++ b/variables.tf @@ -183,7 +183,7 @@ variable "kms_encryption_enabled" { variable "use_default_backup_encryption_key" { type = bool - description = "Whether to use the IBM Cloud Databases generated keys." + description = "Whether to use the IBM Cloud Databases generated keys for backup encryption." default = false } @@ -204,7 +204,7 @@ variable "kms_key_crn" { 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. Applies only if `kms_encryption_enabled` is true. Limitations exist for regions. For more information, see [Key Protect integration](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok) or [Hyper Protect Crypto Services integration](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-hpcs#use-hpcs-backups)." + description = "The CRN of a Hyper Protect Crypto Services use for encrypting the disk that holds deployment backups. 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" default = null validation {