Skip to content

Commit

Permalink
feat: added support to create a root key in a KMS instance that exist…
Browse files Browse the repository at this point in the history
…s in a different account using the option variable `ibmcloud_kms_api_key` (#230)
  • Loading branch information
kierramarie authored Aug 13, 2024
1 parent d322563 commit 19bcf33
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 12 deletions.
45 changes: 43 additions & 2 deletions solutions/standard/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion solutions/standard/provider.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
13 changes: 10 additions & 3 deletions solutions/standard/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand Down Expand Up @@ -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
########################################################################################################################
Expand Down Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion tests/other_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
14 changes: 9 additions & 5 deletions tests/pr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 19bcf33

Please sign in to comment.