diff --git a/README.md b/README.md index 577119ed..40be36d9 100644 --- a/README.md +++ b/README.md @@ -71,17 +71,21 @@ To attach access management tags to resources in this module, you need the follo | 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.postgresql_db](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.postgresql_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 | | [ibm_database_point_in_time_recovery.source_db_earliest_pitr_time](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/data-sources/database_point_in_time_recovery) | data source | @@ -93,12 +97,10 @@ To attach access management tags to resources in this module, you need the follo | [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://ibm.biz/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 Hyper Protect Crypto Services (HPCS) or Key Protect root key CRN to use for encrypting the disk that holds deployment backups. Only used if var.kms\_encryption\_enabled is set to true. 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 | +| [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 | | [configuration](#input\_configuration) | Database configuration parameters, see https://cloud.ibm.com/docs/databases-for-postgresql?topic=databases-for-postgresql-changing-configuration&interface=api for more details. |
object({
shared_buffers = optional(number)
max_connections = optional(number)
# below field gives error when sent to provider
# tracking issue: https://github.com/IBM-Cloud/terraform-provider-ibm/issues/5403
# max_locks_per_transaction = optional(number)
max_prepared_transactions = optional(number)
synchronous_commit = optional(string)
effective_io_concurrency = optional(number)
deadlock_timeout = optional(number)
log_connections = optional(string)
log_disconnections = optional(string)
log_min_duration_statement = optional(number)
tcp_keepalives_idle = optional(number)
tcp_keepalives_interval = optional(number)
tcp_keepalives_count = optional(number)
archive_timeout = optional(number)
wal_level = optional(string)
max_replication_slots = optional(number)
max_wal_senders = optional(number)
})
| `null` | 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 | -| [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\_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-postgresql?topic=databases-for-postgresql-resources-scaling). Ignored during restore and point in time recovery operations | `number` | `0` | no | | [member\_disk\_mb](#input\_member\_disk\_mb) | Allocated disk per member. [Learn more](https://cloud.ibm.com/docs/databases-for-postgresql?topic=databases-for-postgresql-resources-scaling). Ignored during restore and point in time recovery operations | `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). Ignored during restore and point in time recovery operations | `string` | `null` | no | @@ -114,8 +116,10 @@ To attach access management tags to resources in this module, you need the follo | [resource\_tags](#input\_resource\_tags) | Optional list of tags to be added to the PostgreSQL instance. | `list(string)` | `[]` | no | | [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 | | [service\_endpoints](#input\_service\_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 | -| [skip\_iam\_authorization\_policy](#input\_skip\_iam\_authorization\_policy) | Set to true to skip the creation of an IAM authorization policy that permits all PostgreSQL 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 | -| [use\_default\_backup\_encryption\_key](#input\_use\_default\_backup\_encryption\_key) | Set to true to use default ICD randomly generated keys for backup encryption. | `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 PostgreSQL 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 | +| [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 Postgres instance. This blocks creates native postgres database users, more info on that can be found here https://cloud.ibm.com/docs/databases-for-postgresql?topic=databases-for-postgresql-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 02ae2ae2..6ef1cc5c 100644 --- a/cra-config.yaml +++ b/cra-config.yaml @@ -5,8 +5,7 @@ CRA_TARGETS: 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_kms_instance_crn: "crn:v1:bluemix:public:hs-crypto:us-south:a/abac0df06b644a9cabc6e44f55b3880e: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_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_prefix: "test-postgres-standard" TF_VAR_resource_group_name: "test" TF_VAR_provider_visibility: "public" diff --git a/examples/complete/main.tf b/examples/complete/main.tf index 4ce973a3..d6b26138 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -14,6 +14,11 @@ module "resource_group" { # Key Protect All Inclusive ############################################################################## +locals { + data_key_name = "${var.prefix}-pg" + backups_key_name = "${var.prefix}-pg-backups" +} + module "key_protect_all_inclusive" { source = "terraform-ibm-modules/kms-all-inclusive/ibm" version = "4.18.1" @@ -28,7 +33,11 @@ module "key_protect_all_inclusive" { key_ring_name = "icd-pg" keys = [ { - key_name = "${var.prefix}-pg" + key_name = local.data_key_name + force_delete = true + }, + { + key_name = local.backups_key_name force_delete = true } ] @@ -93,20 +102,27 @@ module "cbr_zone" { ############################################################################## module "postgresql_db" { - source = "../../" - resource_group_id = module.resource_group.resource_group_id - name = "${var.prefix}-postgres" - region = var.region - pg_version = var.pg_version - admin_pass = var.admin_pass - users = var.users - kms_encryption_enabled = true - kms_key_crn = module.key_protect_all_inclusive.keys["icd-pg.${var.prefix}-pg"].crn - existing_kms_instance_guid = module.key_protect_all_inclusive.kms_guid - resource_tags = var.resource_tags - service_credential_names = var.service_credential_names - access_tags = var.access_tags - member_host_flavor = "multitenant" + source = "../../" + resource_group_id = module.resource_group.resource_group_id + name = "${var.prefix}-postgres" + region = var.region + pg_version = var.pg_version + admin_pass = var.admin_pass + users = var.users + # 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-pg.${var.prefix}-pg"].crn + backup_encryption_key_crn = module.key_protect_all_inclusive.keys["icd-pg.${local.data_key_name}"].crn + resource_tags = var.resource_tags + service_credential_names = { + "postgressql_admin" : "Administrator", + "postgressql_operator" : "Operator", + "postgressql_viewer" : "Viewer", + "postgressql_editor" : "Editor", + } + access_tags = var.access_tags + member_host_flavor = "multitenant" # Example of setting configuration - none of the below is mandatory - those settings are set in this example for illustation purpose and ensure path is exercised in automated test using this example. configuration = { shared_buffers = 32000 diff --git a/examples/complete/variables.tf b/examples/complete/variables.tf index e8bf7eb9..babfe6ce 100644 --- a/examples/complete/variables.tf +++ b/examples/complete/variables.tf @@ -56,14 +56,3 @@ variable "users" { sensitive = true description = "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." } - -variable "service_credential_names" { - description = "Map of name, role for service credentials that you want to create for the database" - type = map(string) - default = { - "postgressql_admin" : "Administrator", - "postgressql_operator" : "Operator", - "postgressql_viewer" : "Viewer", - "postgressql_editor" : "Editor", - } -} diff --git a/examples/fscloud/catalogValidationValues.json.template b/examples/fscloud/catalogValidationValues.json.template index fc57102b..eaf940f5 100644 --- a/examples/fscloud/catalogValidationValues.json.template +++ b/examples/fscloud/catalogValidationValues.json.template @@ -3,6 +3,5 @@ "region": "us-south", "resource_tags": $TAGS, "prefix": $PREFIX, - "existing_kms_instance_guid": $HPCS_US_SOUTH_GUID, "kms_key_crn": $HPCS_US_SOUTH_ROOT_KEY_CRN } diff --git a/examples/fscloud/main.tf b/examples/fscloud/main.tf index 75899afe..c6621364 100644 --- a/examples/fscloud/main.tf +++ b/examples/fscloud/main.tf @@ -54,20 +54,32 @@ module "cbr_zone" { ############################################################################## module "postgresql_db" { - source = "../../modules/fscloud" - resource_group_id = module.resource_group.resource_group_id - name = "${var.prefix}-postgres" - region = var.region - pg_version = var.pg_version - kms_key_crn = var.kms_key_crn - backup_encryption_key_crn = var.backup_encryption_key_crn - backup_crn = var.backup_crn - existing_kms_instance_guid = var.existing_kms_instance_guid - resource_tags = var.resource_tags - service_credential_names = var.service_credential_names - access_tags = var.access_tags - auto_scaling = var.auto_scaling - member_host_flavor = "b3c.4x16.encrypted" + source = "../../modules/fscloud" + resource_group_id = module.resource_group.resource_group_id + name = "${var.prefix}-postgres" + region = var.region + pg_version = var.pg_version + kms_key_crn = var.kms_key_crn + backup_encryption_key_crn = var.backup_encryption_key_crn + backup_crn = var.backup_crn + resource_tags = var.resource_tags + service_credential_names = { + "postgressql_admin" : "Administrator", + "postgressql_operator" : "Operator", + "postgressql_viewer" : "Viewer", + "postgressql_editor" : "Editor", + } + access_tags = var.access_tags + auto_scaling = { + disk = { + capacity_enabled : true, + io_enabled : true + } + memory = { + io_enabled : true, + } + } + member_host_flavor = "b3c.4x16.encrypted" cbr_rules = [ { description = "${var.prefix}-postgres access only from vpc" diff --git a/examples/fscloud/variables.tf b/examples/fscloud/variables.tf index d523369a..e65037b6 100644 --- a/examples/fscloud/variables.tf +++ b/examples/fscloud/variables.tf @@ -38,11 +38,6 @@ variable "pg_version" { default = null } -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 PostgreSQL database." @@ -60,49 +55,3 @@ variable "backup_crn" { 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 } - -variable "service_credential_names" { - description = "Map of name, role for service credentials that you want to create for the database" - type = map(string) - default = { - "postgressql_admin" : "Administrator", - "postgressql_operator" : "Operator", - "postgressql_viewer" : "Viewer", - "postgressql_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://ibm.biz/autoscaling-considerations in the IBM Cloud Docs." - default = { - disk = { - capacity_enabled : true, - io_enabled : true - } - memory = { - io_enabled : true, - } - } -} diff --git a/examples/pitr/main.tf b/examples/pitr/main.tf index e1c7e394..e2e91df5 100644 --- a/examples/pitr/main.tf +++ b/examples/pitr/main.tf @@ -26,5 +26,4 @@ module "postgresql_db_pitr" { pg_version = var.pg_version pitr_id = var.pitr_id pitr_time = var.pitr_time - } diff --git a/main.tf b/main.tf index 021c3ad6..df321038 100644 --- a/main.tf +++ b/main.tf @@ -1,23 +1,28 @@ ############################################################################## # ICD PostgreSQL module -############################################################################## + +# Input 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 { # 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_pitr_vars = (var.pitr_id != null && var.pitr_time == null) || (var.pitr_time != null && var.pitr_id == null) ? tobool("To use Point-In-Time Recovery (PITR), values for both var.pitr_id and var.pitr_time need to be set. Otherwise, unset both of these.") : 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_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_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_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_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 # 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_pitr_vars = (var.pitr_id != null && var.pitr_time == null) || (var.pitr_time != null && var.pitr_id == null) ? tobool("To use Point-In-Time Recovery (PITR), values for both var.pitr_id and var.pitr_time need to be set. Otherwise, unset both of these.") : 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] @@ -27,33 +32,145 @@ locals { # Determine if restore, from backup or point in time recovery recovery_mode = var.backup_crn != null || var.pitr_id != null +} + +######################################################################################################################## +# 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 +} + +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 +} - # 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" : null - ) - ) : null +# 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 PostgreSQL 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-postgresql" - 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 ICD Postgres 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-postgresql" + source_resource_group_id = var.resource_group_id + roles = ["Reader"] + description = "Allow all PostgreSQL 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-postgresql" + source_resource_group_id = var.resource_group_id + roles = ["Reader"] + description = "Allow all PostgreSQL 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" +} + +######################################################################################################################## +# Postgresql instance +######################################################################################################################## + # Create postgresql database resource "ibm_database" "postgresql_db" { depends_on = [time_sleep.wait_for_authorization_policy] diff --git a/modules/fscloud/README.md b/modules/fscloud/README.md index 35d0b868..7420a5a1 100644 --- a/modules/fscloud/README.md +++ b/modules/fscloud/README.md @@ -34,11 +34,10 @@ 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://ibm.biz/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 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 | +| [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 | | [configuration](#input\_configuration) | Database configuration parameters, see https://cloud.ibm.com/docs/databases-for-postgresql?topic=databases-for-postgresql-changing-configuration&interface=api for more details. |
object({
shared_buffers = optional(number)
max_connections = optional(number)
# below field gives error when sent to provider
# tracking issue: https://github.com/IBM-Cloud/terraform-provider-ibm/issues/5403
# max_locks_per_transaction = optional(number)
max_prepared_transactions = optional(number)
synchronous_commit = optional(string)
effective_io_concurrency = optional(number)
deadlock_timeout = optional(number)
log_connections = optional(string)
log_disconnections = optional(string)
log_min_duration_statement = optional(number)
tcp_keepalives_idle = optional(number)
tcp_keepalives_interval = optional(number)
tcp_keepalives_count = optional(number)
archive_timeout = optional(number)
wal_level = optional(string)
max_replication_slots = optional(number)
max_wal_senders = optional(number)
})
| `null` | no | -| [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 | +| [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\_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-postgresql?topic=databases-for-postgresql-resources-scaling) | `number` | `3` | no | | [member\_disk\_mb](#input\_member\_disk\_mb) | Allocated disk per member. [Learn more](https://cloud.ibm.com/docs/databases-for-postgresql?topic=databases-for-postgresql-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 | @@ -50,8 +49,10 @@ No resources. | [resource\_group\_id](#input\_resource\_group\_id) | The resource group ID where the PostgreSQL instance will be created. | `string` | n/a | yes | | [resource\_tags](#input\_resource\_tags) | Optional list of tags to be added to the PostgreSQL instance. | `list(string)` | `[]` | no | | [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 PostgreSQL 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 | +| [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 PostgreSQL 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 | +| [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 Postgres instance. This blocks creates native postgres database users, more info on that can be found here https://cloud.ibm.com/docs/databases-for-postgresql?topic=databases-for-postgresql-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 a5e05a3d..317b464c 100644 --- a/modules/fscloud/main.tf +++ b/modules/fscloud/main.tf @@ -1,34 +1,28 @@ -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 "postgresql_db" { - source = "../../" - resource_group_id = var.resource_group_id - name = var.name - region = var.region - skip_iam_authorization_policy = var.skip_iam_authorization_policy - service_endpoints = "private" - pg_version = var.pg_version - 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_encryption_key_crn = var.backup_encryption_key_crn - resource_tags = var.resource_tags - access_tags = var.access_tags - cbr_rules = var.cbr_rules - configuration = var.configuration - member_memory_mb = var.member_memory_mb - member_disk_mb = var.member_disk_mb - member_cpu_count = var.member_cpu_count - member_host_flavor = var.member_host_flavor - members = var.members - admin_pass = var.admin_pass - users = var.users - service_credential_names = var.service_credential_names - auto_scaling = var.auto_scaling - backup_crn = var.backup_crn + source = "../../" + resource_group_id = var.resource_group_id + name = var.name + region = var.region + skip_iam_authorization_policy = var.skip_iam_authorization_policy + service_endpoints = "private" + pg_version = var.pg_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 + resource_tags = var.resource_tags + access_tags = var.access_tags + cbr_rules = var.cbr_rules + configuration = var.configuration + member_memory_mb = var.member_memory_mb + member_disk_mb = var.member_disk_mb + member_cpu_count = var.member_cpu_count + member_host_flavor = var.member_host_flavor + members = var.members + admin_pass = var.admin_pass + users = var.users + service_credential_names = var.service_credential_names + auto_scaling = var.auto_scaling } diff --git a/modules/fscloud/variables.tf b/modules/fscloud/variables.tf index bab0167c..05a2670d 100644 --- a/modules/fscloud/variables.tf +++ b/modules/fscloud/variables.tf @@ -164,26 +164,48 @@ variable "use_ibm_owned_encryption_key" { variable "kms_key_crn" { type = string - description = "The Hyper Protect Crypto Services (HPCS) or Key Protect root key CRN 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 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)." + 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.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 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 PostgreSQL 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." + 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 (HPCS) or Key Protect instance." - default = null +variable "skip_iam_authorization_policy" { + type = bool + description = "Set to true to skip the creation of IAM authorization policies that permits all Databases for PostgreSQL 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/solutions/standard/main.tf b/solutions/standard/main.tf index 982bbc5f..adabea31 100644 --- a/solutions/standard/main.tf +++ b/solutions/standard/main.tf @@ -1,21 +1,3 @@ -####################################################################################################################### -# Local Variables -####################################################################################################################### - -locals { - existing_kms_instance_guid = var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].service_instance : null - existing_kms_instance_region = var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].region : null - - key_name = var.prefix != null ? "${var.prefix}-${var.key_name}" : var.key_name - key_ring_name = var.prefix != null ? "${var.prefix}-${var.key_ring_name}" : var.key_ring_name - - kms_key_crn = !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.key_ring_name, local.key_name)].crn : null - - create_cross_account_auth_policy = !var.skip_iam_authorization_policy && var.ibmcloud_kms_api_key != null && !var.use_ibm_owned_encryption_key - - kms_service_name = var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].service_name : null -} - ####################################################################################################################### # Resource Group ####################################################################################################################### @@ -28,57 +10,48 @@ module "resource_group" { } ####################################################################################################################### -# KMS root key for Postgresql +# 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 ####################################################################################################################### -data "ibm_iam_account_settings" "iam_account_settings" { - count = local.create_cross_account_auth_policy ? 1 : 0 -} -# If existing KMS intance CRN passed, parse details from it -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 +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 } -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-postgresql" - 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 Postgresql 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}" -} +####################################################################################################################### +# KMS encryption key +####################################################################################################################### -# workaround for https://github.com/IBM-Cloud/terraform-provider-ibm/issues/4478 -resource "time_sleep" "wait_for_authorization_policy" { - depends_on = [ibm_iam_authorization_policy.kms_policy] - create_duration = "30s" +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 + postgres_key_name = var.prefix != null ? "${var.prefix}-${var.key_name}" : var.key_name + postgres_key_ring_name = var.prefix != null ? "${var.prefix}-${var.key_ring_name}" : var.key_ring_name } module "kms" { providers = { ibm = ibm.kms } - count = var.existing_kms_key_crn != null || 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 + count = local.create_new_kms_key ? 1 : 0 source = "terraform-ibm-modules/kms-all-inclusive/ibm" version = "4.18.1" create_key_protect_instance = false - region = local.existing_kms_instance_region + 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.key_ring_name + key_ring_name = local.postgres_key_ring_name existing_key_ring = false keys = [ { - key_name = local.key_name + key_name = local.postgres_key_name standard_key = false rotation_interval_month = 3 dual_auth_delete_enabled = false @@ -89,103 +62,213 @@ module "kms" { ] } +######################################################################################################################## +# 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 backup encryption key for Postgresql +# KMS IAM Authorization Policies +# - only created if user passes a value for 'ibmcloud_kms_api_key' (used when KMS is in different account to PostgreSQL) +# - if no value passed for 'ibmcloud_kms_api_key', the auth policy is created by the PostgreSQL module ####################################################################################################################### +# Lookup account ID +data "ibm_iam_account_settings" "iam_account_settings" { +} + locals { - existing_backup_kms_instance_guid = var.existing_backup_kms_instance_crn != null ? module.backup_kms_instance_crn_parser[0].service_instance : null - existing_backup_kms_instance_region = var.existing_backup_kms_instance_crn != null ? module.backup_kms_instance_crn_parser[0].region : null + account_id = data.ibm_iam_account_settings.iam_account_settings.account_id + create_cross_account_kms_auth_policy = !var.skip_pg_kms_auth_policy && var.ibmcloud_kms_api_key != null && !var.use_ibm_owned_encryption_key + create_cross_account_backup_kms_auth_policy = !var.skip_pg_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.postgres_key_ring_name, local.postgres_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.postgres_key_ring_name, local.postgres_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_key_name = var.prefix != null ? "${var.prefix}-backup-encryption-${var.key_name}" : "backup-encryption-${var.key_name}" - backup_key_ring_name = var.prefix != null ? "${var.prefix}-backup-encryption-${var.key_ring_name}" : "backup-encryption-${var.key_ring_name}" - backup_kms_key_crn = var.existing_backup_kms_key_crn != null ? var.existing_backup_kms_key_crn : var.existing_backup_kms_instance_crn != null ? module.backup_kms[0].keys[format("%s.%s", local.backup_key_ring_name, local.backup_key_name)].crn : null - backup_kms_service_name = var.existing_backup_kms_instance_crn != null ? module.backup_kms_instance_crn_parser[0].service_name : null + 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 } -# If existing KMS intance CRN passed, parse details from it -module "backup_kms_instance_crn_parser" { - count = var.existing_backup_kms_instance_crn != null ? 1 : 0 - source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser" - version = "1.1.0" - crn = var.existing_backup_kms_instance_crn +# 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-postgresql" + source_resource_group_id = module.resource_group.resource_group_id + roles = ["Reader"] + description = "Allow all PostgreSQL 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.existing_backup_kms_instance_guid == local.existing_kms_instance_guid ? 0 : var.existing_backup_kms_key_crn != null ? 0 : var.existing_backup_kms_instance_crn != null ? !var.skip_iam_authorization_policy ? 1 : 0 : 0 - provider = ibm.kms - source_service_account = local.create_cross_account_auth_policy ? data.ibm_iam_account_settings.iam_account_settings[0].account_id : null - source_service_name = "databases-for-postgresql" - source_resource_group_id = module.resource_group.resource_group_id - target_service_name = local.backup_kms_service_name - target_resource_instance_id = local.existing_backup_kms_instance_guid - roles = ["Reader"] - description = "Allow all Postgresql instances in the resource group ${module.resource_group.resource_group_id} to read from the ${local.backup_kms_service_name} instance GUID ${local.existing_backup_kms_instance_guid}" + 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-postgresql" + source_resource_group_id = module.resource_group.resource_group_id + roles = ["Reader"] + description = "Allow all PostgreSQL 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" } -module "backup_kms" { - providers = { - ibm = ibm.kms - } - count = var.use_ibm_owned_encryption_key ? 0 : var.existing_backup_kms_key_crn != null ? 0 : var.existing_backup_kms_instance_crn != null ? 1 : 0 - source = "terraform-ibm-modules/kms-all-inclusive/ibm" - version = "4.18.1" - create_key_protect_instance = false - region = local.existing_backup_kms_instance_region - existing_kms_instance_crn = var.existing_backup_kms_instance_crn - key_ring_endpoint_type = var.kms_endpoint_type - key_endpoint_type = var.kms_endpoint_type - keys = [ - { - key_ring_name = local.backup_key_ring_name - existing_key_ring = false - force_delete_key_ring = true - keys = [ - { - key_name = local.backup_key_name - standard_key = false - rotation_interval_month = 3 - dual_auth_delete_enabled = false - force_delete = true - } - ] - } - ] +####################################################################################################################### +# PostgreSQL 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 } ####################################################################################################################### # Postgresql ####################################################################################################################### +# Create new instance module "postgresql_db" { - 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 - name = var.prefix != null ? "${var.prefix}-${var.name}" : var.name - region = var.region - skip_iam_authorization_policy = local.create_cross_account_auth_policy ? true : var.skip_iam_authorization_policy - pg_version = var.pg_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 - resource_tags = var.resource_tags - admin_pass = var.admin_pass - users = var.users - members = var.members - member_host_flavor = var.member_host_flavor - member_memory_mb = var.member_memory_mb - member_disk_mb = var.member_disk_mb - member_cpu_count = var.member_cpu_count - auto_scaling = var.auto_scaling - configuration = var.configuration - service_credential_names = var.service_credential_names - backup_encryption_key_crn = local.backup_kms_key_crn - backup_crn = var.backup_crn + 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 + name = var.prefix != null ? "${var.prefix}-${var.name}" : var.name + region = var.region + skip_iam_authorization_policy = var.skip_pg_kms_auth_policy + pg_version = var.pg_version + 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 + resource_tags = var.resource_tags + admin_pass = local.admin_pass + users = var.users + members = var.members + member_host_flavor = var.member_host_flavor + member_memory_mb = var.member_memory_mb + member_disk_mb = var.member_disk_mb + member_cpu_count = var.member_cpu_count + auto_scaling = var.auto_scaling + configuration = var.configuration + service_credential_names = var.service_credential_names + backup_crn = var.backup_crn } diff --git a/solutions/standard/provider.tf b/solutions/standard/provider.tf index b4abce7d..8e9cf064 100644 --- a/solutions/standard/provider.tf +++ b/solutions/standard/provider.tf @@ -8,6 +8,6 @@ provider "ibm" { 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 + region = local.kms_region visibility = var.provider_visibility } diff --git a/solutions/standard/variables.tf b/solutions/standard/variables.tf index 220555f4..d29abf42 100644 --- a/solutions/standard/variables.tf +++ b/solutions/standard/variables.tf @@ -8,13 +8,6 @@ variable "ibmcloud_api_key" { sensitive = true } -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 PostgreSQL instance. Leave this input empty if the same account owns both instances." - sensitive = true - 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 @@ -217,10 +210,28 @@ variable "auto_scaling" { 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." + 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 "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 "key_name" { type = string default = "postgresql-key" @@ -233,6 +244,12 @@ variable "key_ring_name" { description = "The name for the key ring created for the PostgreSQL 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_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 "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`." @@ -243,35 +260,15 @@ variable "kms_endpoint_type" { } } -variable "existing_kms_key_crn" { - type = string - description = "The CRN of an Hyper Protect Crypto Services or Key Protect encryption key that you want to use to use for both disk and backup encryption. If no value is passed, a new key ring and key will be created in the instance provided in the `existing_kms_instance_crn` input. Backup encryption is only supported is some regions ([learn more](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok)), so if you need to use a key from a different region for backup encryption, use the `existing_backup_kms_key_crn` input." - default = null -} - -variable "existing_kms_instance_crn" { - description = "The CRN of an Hyper Protect Crypto Services or Key Protect instance that you want to use for both disk and backup encryption. Backup encryption is only supported is some regions ([learn more](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok)), so if you need to use a different instance for backup encryption from a supported region, use the `existing_backup_kms_instance_crn` input." - type = string - default = null -} - -variable "skip_iam_authorization_policy" { +variable "skip_pg_kms_auth_policy" { type = bool - description = "Whether to create an IAM authorization policy that permits all PostgreSQL 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." + description = "Set to true to skip the creation of IAM authorization policies that permits all Databases for PostgreSQL instances in the given resource group 'Reader' access to the Key Protect or Hyper Protect Crypto Services key. 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 } -############################################################## -# Backup Encryption -############################################################## -variable "existing_backup_kms_key_crn" { - type = string - description = "The CRN of an Hyper Protect Crypto Services or Key Protect encryption key that you want to use to encrypt database backups. If no value is passed, the value of `existing_kms_key_crn` is used. If no value is passed for that, a new key will be created in the provided KMS instance and used for both disk encryption, and backup encryption." - default = null -} - -variable "existing_backup_kms_instance_crn" { - description = "The CRN of an Hyper Protect Crypto Services or Key Protect instance that you want to use to encrypt database backups. If no value is passed, the value of the `existing_kms_instance_crn` input will be used, however backup encryption is only supported in certain regions so you need to ensure the KMS for backup is coming from one of the supported regions. [Learn more](https://cloud.ibm.com/docs/cloud-databases?topic=cloud-databases-key-protect&interface=ui#key-byok)" +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 PostgreSQL instance. Leave this input empty if the same account owns both instances." + sensitive = true default = null } diff --git a/solutions/standard/version.tf b/solutions/standard/version.tf index 381e6a0e..76ce8584 100644 --- a/solutions/standard/version.tf +++ b/solutions/standard/version.tf @@ -11,5 +11,9 @@ terraform { source = "hashicorp/time" version = "0.12.1" } + random = { + source = "hashicorp/random" + version = "3.6.3" + } } } diff --git a/tests/pr_test.go b/tests/pr_test.go index 555e2663..12640483 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -57,10 +57,9 @@ func TestRunFSCloudExample(t *testing.T) { */ //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"], - "pg_version": "16", // Always lock this test into the latest supported Postgres version + "access_tags": permanentResources["accessTags"], + "kms_key_crn": permanentResources["hpcs_south_root_key_crn"], + "pg_version": "16", // Always lock this test into the latest supported Postgres version }, CloudInfoService: sharedInfoSvc, }) diff --git a/variables.tf b/variables.tf index 5a8e757e..888dde6f 100644 --- a/variables.tf +++ b/variables.tf @@ -254,15 +254,15 @@ 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 "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 { @@ -271,39 +271,43 @@ 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 Hyper Protect Crypto Services (HPCS) or Key Protect root key CRN to use for encrypting the disk that holds deployment backups. Only used if var.kms_encryption_enabled is set to true. 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)." + 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.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 in one of the supported backup regions." } } variable "use_default_backup_encryption_key" { type = bool - description = "Set to true to use default ICD randomly generated keys for backup encryption." + 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 "skip_iam_authorization_policy" { type = bool - description = "Set to true to skip the creation of an IAM authorization policy that permits all PostgreSQL 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 PostgreSQL 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) ##############################################################