From 604970a408d7ce20b2bf4a3e12af8fca96bc0c50 Mon Sep 17 00:00:00 2001 From: Chris Waddington <104161708+chrisw-ibm@users.noreply.github.com> Date: Tue, 3 Dec 2024 10:55:46 -0500 Subject: [PATCH] feat: Add CBR's to COS bucket --- README.md | 13 +- ibm_catalog.json | 96 ++++++++++- main.tf | 190 +++++++++++++++++++++- modules/encrypted_cos_bucket/README.md | 4 +- modules/encrypted_cos_bucket/main.tf | 41 +++++ modules/encrypted_cos_bucket/outputs.tf | 12 +- modules/encrypted_cos_bucket/variables.tf | 27 ++- variables.tf | 143 ++++++++++------ 8 files changed, 467 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 2870d8f..4af4e03 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,11 @@ statement instead the previous block. | Name | Source | Version | |------|--------|---------| | [billing\_exports](#module\_billing\_exports) | ./modules/billing-exports | n/a | +| [cbr\_zone\_additional](#module\_cbr\_zone\_additional) | terraform-ibm-modules/cbr/ibm//modules/cbr-zone-module | 1.29.0 | +| [cbr\_zone\_cloudability](#module\_cbr\_zone\_cloudability) | terraform-ibm-modules/cbr/ibm//modules/cbr-zone-module | 1.29.0 | +| [cbr\_zone\_cos](#module\_cbr\_zone\_cos) | terraform-ibm-modules/cbr/ibm//modules/cbr-zone-module | 1.29.0 | +| [cbr\_zone\_ibmcloud\_billing](#module\_cbr\_zone\_ibmcloud\_billing) | terraform-ibm-modules/cbr/ibm//modules/cbr-zone-module | 1.29.0 | +| [cbr\_zone\_schematics](#module\_cbr\_zone\_schematics) | terraform-ibm-modules/cbr/ibm//modules/cbr-zone-module | 1.29.0 | | [cloudability\_bucket\_access](#module\_cloudability\_bucket\_access) | ./modules/cloudability-bucket-access | n/a | | [cloudability\_enterprise\_access](#module\_cloudability\_enterprise\_access) | ./modules/cloudability-enterprise-access | n/a | | [cloudability\_onboarding](#module\_cloudability\_onboarding) | ./modules/cloudability-onboarding | n/a | @@ -130,11 +135,17 @@ statement instead the previous block. | [activity\_tracker\_read\_data\_events](#input\_activity\_tracker\_read\_data\_events) | If set to true, all Object Storage bucket read events (downloads) will be sent to Activity Tracker. | `bool` | `true` | no | | [activity\_tracker\_write\_data\_events](#input\_activity\_tracker\_write\_data\_events) | If set to true, all Object Storage bucket read events (downloads) will be sent to Activity Tracker. | `bool` | `true` | no | | [add\_bucket\_name\_suffix](#input\_add\_bucket\_name\_suffix) | Add random generated suffix (4 characters long) to the newly provisioned Object Storage bucket name (Optional). | `bool` | `true` | no | +| [additional\_allowed\_cbr\_bucket\_ip\_addresses](#input\_additional\_allowed\_cbr\_bucket\_ip\_addresses) | A list of CBR zone IP addresses, which are permitted to access the bucket. This zone typically represents the IP addresses for your company or workstation to allow access to view the contents of the bucket. | `list(string)` | `[]` | no | | [archive\_days](#input\_archive\_days) | Specifies the number of days when the archive rule action takes effect. A value of `null` disables archiving. A value of `0` immediately archives uploaded objects to the bucket. | `number` | `null` | no | | [archive\_type](#input\_archive\_type) | Specifies the storage class or archive type to which you want the object to transition. | `string` | `"Glacier"` | no | -| [bucket\_cbr\_rules](#input\_bucket\_cbr\_rules) | (Optional, list) List of CBR rules to create for the bucket |
list(object({| `[]` | no | | [bucket\_name](#input\_bucket\_name) | The name to give the newly provisioned Object Storage bucket. | `string` | `"billing-reports"` | no | | [bucket\_storage\_class](#input\_bucket\_storage\_class) | The storage class of the newly provisioned Object Storage bucket. Supported values are 'standard', 'vault', 'cold', 'smart' and `onerate_active`. | `string` | `"standard"` | no | +| [cbr\_additional\_zone\_name](#input\_cbr\_additional\_zone\_name) | Name of the CBR zone that corresponds to the ip address range set in `additional_allowed_cbr_bucket_ip_addresses`. | `string` | `"company-billing-reports-bucket-access"` | no | +| [cbr\_billing\_zone\_name](#input\_cbr\_billing\_zone\_name) | Name of the CBR zone which represents IBM Cloud billing. See [What are CBRs?](https://cloud.ibm.com/docs/account?topic=account-context-restrictions-whatis) | `string` | `"ibmcloud-billing-reports-bucket-writer"` | no | +| [cbr\_cloudability\_zone\_name](#input\_cbr\_cloudability\_zone\_name) | Name of the CBR zone which represents IBM Cloudability. See [What are CBRs?](https://cloud.ibm.com/docs/account?topic=account-context-restrictions-whatis) | `string` | `"cldy-billing-reports-bucket-reader"` | no | +| [cbr\_cos\_zone\_name](#input\_cbr\_cos\_zone\_name) | Name of the CBR zone which represents Cloud Object Storage service. See [What are CBRs?](https://cloud.ibm.com/docs/account?topic=account-context-restrictions-whatis) | `string` | `"cldy-billing-reports-object-storage"` | no | +| [cbr\_enforcement\_mode](#input\_cbr\_enforcement\_mode) | The rule enforcement mode: * enabled - The restrictions are enforced and reported. This is the default. * disabled - The restrictions are disabled. Nothing is enforced or reported. * report - The restrictions are evaluated and reported, but not enforced. | `string` | `"enabled"` | no | +| [cbr\_schematics\_zone\_name](#input\_cbr\_schematics\_zone\_name) | Name of the CBR zone which represents Schematics. The schematics zone allows Projects to access and manage the Object Storage bucket. | `string` | `"schematics-billing-reports-bucket-management"` | no | | [cloudability\_api\_key](#input\_cloudability\_api\_key) | Cloudability API Key. Retrieve your Api Key from https://app.apptio.com/cloudability#/settings/preferences under the section **Cloudability API** select **Enable API** which will generate an api key. Setting this value to __NULL__ will skip adding the IBM Cloud account to Cloudability and only configure IBM Cloud so that the IBM Cloud Account can be added to Cloudability manually | `string` | `null` | no | | [cloudability\_auth\_type](#input\_cloudability\_auth\_type) | Select Cloudability authentication mode. Options are:
description = string
account_id = string
rule_contexts = list(object({
attributes = optional(list(object({
name = string
value = string
}))) }))
enforcement_mode = string
tags = optional(list(object({
name = string
value = string
})), [])
operations = optional(list(object({
api_types = list(object({
api_type_id = string
}))
})))
}))
list(object({| `[]` | no | | [management\_endpoint\_type\_for\_bucket](#input\_management\_endpoint\_type\_for\_bucket) | The type of endpoint for the IBM terraform provider to use to manage the bucket. (public, private, or direct) | `string` | `"public"` | no | | [monitoring\_crn](#input\_monitoring\_crn) | The CRN of an IBM Cloud Monitoring instance to send Object Storage bucket metrics to. If no value passed, metrics are sent to the instance associated to the container's location unless otherwise specified in the Metrics Router service configuration. | `string` | `null` | no | | [object\_versioning\_enabled](#input\_object\_versioning\_enabled) | Enable object versioning to keep multiple versions of an object in a bucket. | `bool` | `false` | no | @@ -129,13 +131,13 @@ No resources. | Name | Description | |------|-------------| +| [bucket\_cbr\_rule\_ids](#output\_bucket\_cbr\_rule\_ids) | Object Storage bucket rule ids | | [bucket\_cbr\_rules](#output\_bucket\_cbr\_rules) | Object Storage bucket rules | | [bucket\_crn](#output\_bucket\_crn) | Bucket CRN | | [bucket\_id](#output\_bucket\_id) | Bucket id | | [bucket\_name](#output\_bucket\_name) | Bucket name | | [bucket\_region](#output\_bucket\_region) | Bucket region if you create a regional bucket | | [bucket\_storage\_class](#output\_bucket\_storage\_class) | Bucket Storage Class | -| [cbr\_rule\_ids](#output\_cbr\_rule\_ids) | List of all rule ids | | [cos\_instance\_guid](#output\_cos\_instance\_guid) | The GUID of the Cloud Object Storage Instance where the buckets are created | | [cos\_instance\_id](#output\_cos\_instance\_id) | The ID of the Cloud Object Storage Instance where the buckets are created | | [instance\_cbr\_rules](#output\_instance\_cbr\_rules) | COS instance rules | diff --git a/modules/encrypted_cos_bucket/main.tf b/modules/encrypted_cos_bucket/main.tf index c24b421..0d7d105 100644 --- a/modules/encrypted_cos_bucket/main.tf +++ b/modules/encrypted_cos_bucket/main.tf @@ -66,6 +66,47 @@ module "key_protect_all_inclusive" { access_tags = var.access_tags } +locals { + default_operations = [{ + api_types = [{ + api_type_id = "crn:v1:bluemix:public:context-based-restrictions::::api-type:" + }] + }] +} + +module "key_protect_key_cbr_rule" { + count = length(var.kms_key_cbr_rules) > 0 ? length(var.kms_key_cbr_rules) : 0 + source = "terraform-ibm-modules/cbr/ibm//modules/cbr-rule-module" + version = "1.29.0" + rule_description = var.kms_key_cbr_rules[count.index].description + enforcement_mode = var.kms_key_cbr_rules[count.index].enforcement_mode + rule_contexts = var.kms_key_cbr_rules[count.index].rule_contexts + resources = [{ + attributes = [ + { + name = "accountId" + value = var.kms_key_cbr_rules[count.index].account_id + }, + { + name = "serviceInstance" + value = local.existing_kms_instance_guid + operator = "stringEquals" + }, + { + name = "serviceName" + value = "kms" + }, + { + name = "resource" + value = local.key_name + operator = "stringEquals" + } + ], + tags = var.kms_key_cbr_rules[count.index].tags + }] + operations = var.kms_key_cbr_rules[count.index].operations == null ? local.default_operations : var.kms_key_cbr_rules[count.index].operations +} + ############################################################################## # Get Cloud Account ID ############################################################################## diff --git a/modules/encrypted_cos_bucket/outputs.tf b/modules/encrypted_cos_bucket/outputs.tf index e486fe5..a621fba 100644 --- a/modules/encrypted_cos_bucket/outputs.tf +++ b/modules/encrypted_cos_bucket/outputs.tf @@ -57,8 +57,8 @@ output "instance_cbr_rules" { value = module.cos_bucket.instance_cbr_rules } -output "cbr_rule_ids" { - description = "List of all rule ids" +output "bucket_cbr_rule_ids" { + description = "Object Storage bucket rule ids" value = module.cos_bucket.cbr_rule_ids } output "bucket_cbr_rules" { @@ -88,20 +88,20 @@ output "key_protect_guid" { output "key_protect_name" { description = "Key Protect Name" - value = local.key_management_enabled ? module.key_protect_all_inclusive[0].key_protect_name : null + value = module.key_protect_all_inclusive.key_protect_name } output "key_protect_instance_policies" { description = "Instance Polices of the Key Protect instance" - value = local.key_management_enabled ? module.key_protect_all_inclusive[0].key_protect_instance_policies : null + value = module.key_protect_all_inclusive.key_protect_instance_policies } output "key_rings" { description = "IDs of new Key Rings created by the module" - value = local.key_management_enabled ? module.key_protect_all_inclusive[0].key_rings : null + value = module.key_protect_all_inclusive.key_rings } output "keys" { description = "IDs of new Keys created by the module" - value = local.key_management_enabled ? module.key_protect_all_inclusive[0].keys : null + value = module.key_protect_all_inclusive.keys } diff --git a/modules/encrypted_cos_bucket/variables.tf b/modules/encrypted_cos_bucket/variables.tf index 9915c88..2076d53 100644 --- a/modules/encrypted_cos_bucket/variables.tf +++ b/modules/encrypted_cos_bucket/variables.tf @@ -297,7 +297,7 @@ variable "cos_bucket_cbr_rules" { # Validation happens in the rule module } -variable "cos_instance_cbr_rules" { +variable "instance_cbr_rules" { type = list(object({ description = string account_id = string @@ -364,6 +364,31 @@ variable "rotation_interval_month" { default = 1 } +variable "kms_key_cbr_rules" { + type = list(object({ + description = string + account_id = string + rule_contexts = list(object({ + attributes = optional(list(object({ + name = string + value = string + }))) })) + enforcement_mode = string + tags = optional(list(object({ + name = string + value = string + })), []) + operations = optional(list(object({ + api_types = list(object({ + api_type_id = string + })) + }))) + })) + description = "(Optional, list) List of CBR rules to create for the instance" + default = [] + # Validation happens in the rule module +} + variable "skip_iam_authorization_policy" { type = bool description = "Set to true to skip the creation of an IAM authorization policy that permits the COS instance created to read the encryption key from the KMS instance in `existing_kms_instance_crn`. WARNING: An authorization policy must exist before an encrypted bucket can be created" diff --git a/variables.tf b/variables.tf index c5bf86f..e35c3a7 100644 --- a/variables.tf +++ b/variables.tf @@ -345,6 +345,7 @@ variable "existing_kms_instance_crn" { } } + variable "key_protect_allowed_network" { type = string description = "The type of the allowed network to be set for the Key Protect instance. Possible values are 'private-only', or 'public-and-private'. Only used if 'create_key_protect_instance' is true." @@ -355,59 +356,107 @@ variable "key_protect_allowed_network" { } } +variable "kms_endpoint_type" { + type = string + description = "The type of endpoint to be used for management of key protect." + default = "public" + validation { + condition = can(regex("public|private", var.kms_endpoint_type)) + error_message = "The endpoint_type value must be 'public' or 'private'." + } +} + +variable "kms_rotation_enabled" { + type = bool + description = "If set to true, Key Protect enables a rotation policy on the Key Protect instance. Only used if 'create_key_protect_instance' is true." + default = true +} + +variable "kms_rotation_interval_month" { + type = number + description = "Specifies the number of months for the encryption key to be rotated.. Must be between 1 and 12 inclusive." + default = 1 + validation { + condition = var.kms_rotation_interval_month >= 1 && var.kms_rotation_interval_month <= 12 + error_message = "The key rotation time interval must be greater than 0 and less than 13" + } +} + + ############################################################## # Context-based restriction (CBR) ############################################################## -variable "bucket_cbr_rules" { - type = list(object({ - description = string - account_id = string - rule_contexts = list(object({ - attributes = optional(list(object({ - name = string - value = string - }))) })) - enforcement_mode = string - tags = optional(list(object({ - name = string - value = string - })), []) - operations = optional(list(object({ - api_types = list(object({ - api_type_id = string - })) - }))) - })) - description = "(Optional, list) List of CBR rules to create for the bucket" - default = [] - # Validation happens in the rule module -} - -variable "instance_cbr_rules" { - type = list(object({ - description = string - account_id = string - rule_contexts = list(object({ - attributes = optional(list(object({ - name = string - value = string - }))) })) - enforcement_mode = string - tags = optional(list(object({ - name = string - value = string - })), []) - operations = optional(list(object({ - api_types = list(object({ - api_type_id = string - })) - }))) - })) - description = "(Optional, list) List of CBR rules to create for the instance" +variable "cbr_enforcement_mode" { + type = string + description = "The rule enforcement mode: * enabled - The restrictions are enforced and reported. This is the default. * disabled - The restrictions are disabled. Nothing is enforced or reported. * report - The restrictions are evaluated and reported, but not enforced." + default = "enabled" + validation { + condition = contains(["enabled", "disabled", "report"], var.cbr_enforcement_mode) + error_message = "Invalid cbr_enforcement_mode: use one of \"enabled\", \"disabled\", or \"report\". See CBR Rule Enforcement docs for more details: https://cloud.ibm.com/docs/account?topic=account-context-restrictions-whatis&interface=ui#rule-enforcement" + } +} +variable "cbr_billing_zone_name" { + type = string + description = "Name of the CBR zone which represents IBM Cloud billing. See [What are CBRs?](https://cloud.ibm.com/docs/account?topic=account-context-restrictions-whatis)" + default = "ibmcloud-billing-reports-bucket-writer" + validation { + condition = can(regex("^[a-zA-Z0-9 -_]{1,128}$", var.cbr_billing_zone_name)) + error_message = "Invalid `cbr_billing_zone_name`: value must meet the following regular expression /^[a-zA-Z0-9 -_]+$/ and have length > 1 and < 128" + } +} + +variable "cbr_cloudability_zone_name" { + type = string + description = "Name of the CBR zone which represents IBM Cloudability. See [What are CBRs?](https://cloud.ibm.com/docs/account?topic=account-context-restrictions-whatis)" + default = "cldy-billing-reports-bucket-reader" + validation { + condition = can(regex("^[a-zA-Z0-9 -_]{1,128}$", var.cbr_cloudability_zone_name)) + error_message = "Invalid `cbr_cloudability_zone_name`: value must meet the following regular expression /^[a-zA-Z0-9 -_]+$/ and have length > 1 and < 128" + } +} + +variable "cbr_cos_zone_name" { + type = string + description = "Name of the CBR zone which represents Cloud Object Storage service. See [What are CBRs?](https://cloud.ibm.com/docs/account?topic=account-context-restrictions-whatis)" + default = "cldy-billing-reports-object-storage" + validation { + condition = can(regex("^[a-zA-Z0-9 -_]{1,128}$", var.cbr_cos_zone_name)) + error_message = "Invalid `cbr_cloudability_zone_name`: value must meet the following regular expression /^[a-zA-Z0-9 -_]+$/ and have length > 1 and < 128" + } +} + +variable "cbr_schematics_zone_name" { + type = string + description = "Name of the CBR zone which represents Schematics. The schematics zone allows Projects to access and manage the Object Storage bucket." + default = "schematics-billing-reports-bucket-management" + validation { + condition = can(regex("^[a-zA-Z0-9 -_]{1,128}$", var.cbr_schematics_zone_name)) + error_message = "Invalid `cbr_schematics_zone_name`: value must meet the following regular expression /^[a-zA-Z0-9 -_]+$/ and have length > 1 and < 128" + } +} + +variable "cbr_additional_zone_name" { + type = string + description = "Name of the CBR zone that corresponds to the ip address range set in `additional_allowed_cbr_bucket_ip_addresses`." + default = "company-billing-reports-bucket-access" + validation { + condition = can(regex("^[a-zA-Z0-9 -_]{1,128}$", var.cbr_additional_zone_name)) + error_message = "Invalid `cbr_additional_zone_name`: value must meet the following regular expression /^[a-zA-Z0-9 -_]+$/ and have length > 1 and < 128" + } +} + +variable "additional_allowed_cbr_bucket_ip_addresses" { + type = list(string) + description = "A list of CBR zone IP addresses, which are permitted to access the bucket. This zone typically represents the IP addresses for your company or workstation to allow access to view the contents of the bucket." default = [] - # Validation happens in the rule module +} + +variable "existing_allowed_cbr_bucket_zone_id" { + type = string + description = "An extra CBR zone ID which is permitted to access the bucket. This zone typically represents the ip addresses for your company or workstation to allow access to view the contents of the bucket. It can be used as an alternative to `additional_allowed_cbr_bucket_ip_addresses` in the case that a zone exists." + default = null }
description = string
account_id = string
rule_contexts = list(object({
attributes = optional(list(object({
name = string
value = string
}))) }))
enforcement_mode = string
tags = optional(list(object({
name = string
value = string
})), [])
operations = optional(list(object({
api_types = list(object({
api_type_id = string
}))
})))
}))