diff --git a/solutions/standard/main.tf b/solutions/standard/main.tf index 69ff7c68..fb79ea37 100644 --- a/solutions/standard/main.tf +++ b/solutions/standard/main.tf @@ -19,12 +19,53 @@ locals { parsed_existing_kms_instance_crn = var.existing_kms_instance_crn != null ? split(":", var.existing_kms_instance_crn) : [] kms_region = length(local.parsed_existing_kms_instance_crn) > 0 ? local.parsed_existing_kms_instance_crn[5] : null kms_instance_guid = var.existing_kms_instance_crn != null ? element(split(":", var.existing_kms_instance_crn), length(split(":", var.existing_kms_instance_crn)) - 3) : module.kms[0].kms_instance_guid + create_cross_account_auth_policy = (!var.skip_en_kms_auth_policy || !var.skip_cos_kms_auth_policy) && var.ibmcloud_kms_api_key != null + existing_kms_guid = var.existing_kms_instance_crn != null ? element(split(":", var.existing_kms_instance_crn), length(split(":", var.existing_kms_instance_crn)) - 3) : tobool("The CRN of the existing KMS is not provided.") en_key_name = var.prefix != null ? "${var.prefix}-${var.en_key_name}" : var.en_key_name en_key_ring_name = var.prefix != null ? "${var.prefix}-${var.en_key_ring_name}" : var.en_key_ring_name en_kms_key_id = local.existing_kms_root_key_id != null ? local.existing_kms_root_key_id : module.kms[0].keys[format("%s.%s", local.en_key_ring_name, local.en_key_name)].key_id cos_key_name = var.prefix != null ? "${var.prefix}-${var.cos_key_name}" : var.cos_key_name cos_key_ring_name = var.prefix != null ? "${var.prefix}-${var.cos_key_ring_name}" : var.cos_key_ring_name + cos_instance_guid = var.existing_cos_instance_crn != null ? element(split(":", var.existing_cos_instance_crn), length(split(":", var.existing_cos_instance_crn)) - 3) : null cos_kms_key_crn = var.existing_cos_bucket_name != null ? null : var.existing_kms_root_key_crn != null ? var.existing_kms_root_key_crn : module.kms[0].keys[format("%s.%s", local.cos_key_ring_name, local.cos_key_name)].crn + + kms_service_name = var.existing_kms_instance_crn != null ? ( + can(regex(".*kms.*", var.existing_kms_instance_crn)) ? "kms" : ( + can(regex(".*hs-crypto.*", var.existing_kms_instance_crn)) ? "hs-crypto" : null + ) + ) : null +} + +# Data source to account settings for retrieving cross account id +data "ibm_iam_account_settings" "iam_account_settings" { + count = local.create_cross_account_auth_policy ? 1 : 0 +} + +# Create IAM Authorization Policy to allow COS to access KMS for the encryption key +resource "ibm_iam_authorization_policy" "cos_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 = "cloud-object-storage" + source_resource_instance_id = local.cos_instance_guid + target_service_name = local.kms_service_name + target_resource_instance_id = local.existing_kms_guid + roles = ["Reader"] + description = "Allow the COS instance with GUID ${local.cos_instance_guid} to read from the ${local.kms_service_name} instance GUID ${local.existing_kms_guid}" +} + +# Create IAM Authorization Policy to allow EN to access KMS for the encryption key +resource "ibm_iam_authorization_policy" "en_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 = "event-notifications" + source_resource_instance_id = module.event_notifications[0].guid + target_service_name = local.kms_service_name + target_resource_instance_id = local.existing_kms_guid + roles = ["Reader"] + description = "Allow the EN instance with GUID ${module.event_notifications[0].guid} reader access to the ${local.kms_service_name} instance GUID ${local.existing_kms_guid}" + } # KMS root key for Event Notifications @@ -92,7 +133,7 @@ module "cos" { create_cos_instance = var.existing_cos_instance_crn == null ? true : false create_cos_bucket = var.existing_cos_bucket_name == null ? true : false existing_cos_instance_id = var.existing_cos_instance_crn - skip_iam_authorization_policy = var.skip_cos_kms_auth_policy + skip_iam_authorization_policy = local.create_cross_account_auth_policy || var.skip_cos_kms_auth_policy add_bucket_name_suffix = var.add_bucket_name_suffix resource_group_id = module.resource_group.resource_group_id region = local.cos_bucket_region @@ -145,7 +186,7 @@ module "event_notifications" { kms_endpoint_url = var.kms_endpoint_url existing_kms_instance_crn = local.existing_kms_instance_crn root_key_id = local.en_kms_key_id - skip_en_kms_auth_policy = var.skip_en_kms_auth_policy + skip_en_kms_auth_policy = local.create_cross_account_auth_policy || var.skip_en_kms_auth_policy # COS Related cos_integration_enabled = true cos_bucket_name = local.cos_bucket_name_with_suffix diff --git a/solutions/standard/provider.tf b/solutions/standard/provider.tf index 7e63f1eb..781bedd5 100644 --- a/solutions/standard/provider.tf +++ b/solutions/standard/provider.tf @@ -5,6 +5,6 @@ provider "ibm" { provider "ibm" { alias = "kms" - ibmcloud_api_key = var.ibmcloud_api_key + ibmcloud_api_key = var.ibmcloud_kms_api_key != null ? var.ibmcloud_kms_api_key : var.ibmcloud_api_key region = local.kms_region } diff --git a/solutions/standard/variables.tf b/solutions/standard/variables.tf index a5d9aacf..261a3d16 100644 --- a/solutions/standard/variables.tf +++ b/solutions/standard/variables.tf @@ -98,7 +98,7 @@ variable "existing_en_instance_crn" { variable "existing_kms_instance_crn" { type = string - description = "The CRN of the Hyper Protect Crypto Services or Key Protect instance." + description = "The CRN of the KMS instance (Hyper Protect Crypto Services or Key Protect instance). If the KMS instance is in different account you must also provide a value for `ibmcloud_kms_api_key`." } variable "existing_kms_root_key_crn" { @@ -148,10 +148,17 @@ variable "cos_key_name" { variable "skip_en_kms_auth_policy" { type = bool - description = "Whether an IAM authorization policy is created that permits all Event Notifications instances in the resource group to read the encryption key from the KMS instance. Set to `true` to use an existing policy." + description = "Set to true to skip the creation of an IAM authorization policy that permits the Event Notification instance 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_crn` variable. If a value is specified for `ibmcloud_kms_api_key`, the policy is created in the KMS account." default = false } +variable "ibmcloud_kms_api_key" { + type = string + description = "The IBM Cloud API key that can create a root key and key ring in the key management service (KMS) instance. If not specified, the 'ibmcloud_api_key' variable is used. Specify this key if the instance in `existing_kms_instance_crn` is in an account that's different from the Event Notifications instance. Leave this input empty if the same account owns both instances." + sensitive = true + default = null +} + ######################################################################################################################## # COS ######################################################################################################################## @@ -184,7 +191,7 @@ variable "skip_en_cos_auth_policy" { variable "skip_cos_kms_auth_policy" { type = bool - description = "Whether an IAM authorization policy is created for your Cloud Object Storage instance to read the encryption key from the KMS instance. Set to `true` to use an existing policy." + description = "Set to true to skip the creation of an IAM authorization policy that permits the COS instance 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_crn` variable. If a value is specified for `ibmcloud_kms_api_key`, the policy is created in the KMS account." default = false } diff --git a/tests/other_test.go b/tests/other_test.go index ddf2d3b0..ee69e5f4 100644 --- a/tests/other_test.go +++ b/tests/other_test.go @@ -12,7 +12,7 @@ const basicExampleDir = "examples/basic" func TestRunBasicExample(t *testing.T) { t.Parallel() - options := setupOptions(t, "event-notification-basic", basicExampleDir) + options := setupOptions(t, "en-basic", basicExampleDir) output, err := options.RunTestConsistency() assert.Nil(t, err, "This should not have errored") diff --git a/tests/pr_test.go b/tests/pr_test.go index 71712279..a79466ac 100644 --- a/tests/pr_test.go +++ b/tests/pr_test.go @@ -48,11 +48,15 @@ func TestMain(m *testing.M) { func setupOptions(t *testing.T, prefix string, dir string) *testhelper.TestOptions { options := testhelper.TestOptionsDefaultWithVars(&testhelper.TestOptions{ - Testing: t, - TerraformDir: dir, - Prefix: prefix, - ResourceGroup: resourceGroup, - Region: validRegions[rand.Intn(len(validRegions))], + Testing: t, + TerraformDir: dir, + Prefix: prefix, + /* + Comment out the 'ResourceGroup' input to force this tests to create a unique resource group. This is because + there is a restriction with the Event Notification service, which allows only one Lite plan instance per resource group. + */ + // ResourceGroup: resourceGroup, + Region: validRegions[rand.Intn(len(validRegions))], }) return options