Skip to content

Commit 6101b1e

Browse files
feat: add fully configurable regional bucket DA (#906)
1 parent aa3db06 commit 6101b1e

File tree

12 files changed

+1262
-66
lines changed

12 files changed

+1262
-66
lines changed

.secrets.baseline

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"files": "go.sum|^.secrets.baseline$",
44
"lines": null
55
},
6-
"generated_at": "2025-05-23T10:42:30Z",
6+
"generated_at": "2025-06-23T06:45:07Z",
77
"plugins_used": [
88
{
99
"name": "AWSKeyDetector"
@@ -138,7 +138,7 @@
138138
"hashed_secret": "3e4bdbe0b80e63c22b178576e906810777387b50",
139139
"is_secret": false,
140140
"is_verified": false,
141-
"line_number": 225,
141+
"line_number": 269,
142142
"type": "Secret Keyword",
143143
"verified_result": null
144144
}

ibm_catalog.json

Lines changed: 422 additions & 8 deletions
Large diffs are not rendered by default.

reference-architectures/regional-bucket.svg

Lines changed: 4 additions & 0 deletions
Loading
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Configuring complex inputs for COS - Regional bucket (Fully configurable) in IBM Cloud projects
2+
3+
Several optional input variables in the IBM Cloud [COS - Secure Regional bucket deployable architecture](https://cloud.ibm.com/catalog#deployable_architecture) use complex object types. You specify these inputs when you configure deployable architecture.
4+
5+
* Context-Based Restrictions Rules (`cos_bucket_cbr_rules`)
6+
7+
8+
## Rules For Context-Based Restrictions <a name="cos_bucket_cbr_rules"></a>
9+
10+
The `cos_bucket_cbr_rules` input variable allows you to provide a rule for the target service to enforce access restrictions for the service based on the context of access requests. Contexts are criteria that include the network location of access requests, the endpoint type from where the request is sent, etc.
11+
12+
- Variable name: `cos_bucket_cbr_rules`.
13+
- Type: A list of objects. Allows only one object representing a rule for the target service
14+
- Default value: An empty list (`[]`).
15+
16+
### Options for cos_bucket_cbr_rules
17+
18+
- `description` (required): The description of the rule to create.
19+
- `account_id` (required): The IBM Cloud Account ID
20+
- `rule_contexts` (required): (List) The contexts the rule applies to
21+
- `attributes` (optional): (List) Individual context attributes
22+
- `name` (required): The attribute name.
23+
- `value` (required): The attribute value.
24+
25+
- `enforcement_mode` (required): The rule enforcement mode can have the following values:
26+
- `enabled` - The restrictions are enforced and reported. This is the default.
27+
- `disabled` - The restrictions are disabled. Nothing is enforced or reported.
28+
- `report` - The restrictions are evaluated and reported, but not enforced.
29+
- `tags` (optional): (List) Resource Tags .
30+
- `name` (required): The Tag name.
31+
- `value` (required): The Tag value.
32+
- `operations` (optional): The operations this rule applies to
33+
- `api_types`(required): (List) The API types this rule applies to.
34+
- `api_type_id`(required):The API type ID
35+
36+
### Example Rule For Context-Based Restrictions Configuration
37+
38+
```hcl
39+
{
40+
description = "COS can be accessed from xyz"
41+
account_id = "defc0df06b644a9cabc6e44f55b3880s."
42+
rule_contexts= [{
43+
attributes = [
44+
{
45+
"name" : "endpointType",
46+
"value" : "private"
47+
},
48+
{
49+
name = "networkZoneId"
50+
value = "93a51a1debe2674193217209601dde6f" # pragma: allowlist secret
51+
}
52+
]
53+
}
54+
]
55+
enforcement_mode = "enabled"
56+
resources = [{
57+
tags {
58+
name = "tag_name"
59+
value = "tag_value"
60+
}
61+
}]
62+
operations = [{
63+
api_types = [{
64+
api_type_id = "crn:v1:bluemix:public:context-based-restrictions::::api-type:"
65+
}]
66+
}]
67+
}
68+
```
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Cloud automation for Regional Bucket (Fully configurable)
2+
3+
:exclamation: **Important:** This solution is not intended to be called by other modules because it contains a provider configuration and is not compatible with the `for_each`, `count`, and `depends_on` arguments. For more information, see [Providers Within Modules](https://developer.hashicorp.com/terraform/language/modules/develop/providers).
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"ibmcloud_api_key": $VALIDATION_APIKEY,
3+
"prefix": $PREFIX,
4+
"bucket_name": "test",
5+
"region": "us-south",
6+
"kms_encryption_enabled": true,
7+
"existing_kms_instance_crn": $HPCS_US_SOUTH_CRN,
8+
"existing_cos_instance_crn": $COS_INSTANCE_CRN
9+
}
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
##############################################################################
2+
# Secure Regional Bucket
3+
##############################################################################
4+
locals {
5+
prefix = var.prefix != null ? trimspace(var.prefix) != "" ? "${var.prefix}-" : "" : ""
6+
}
7+
8+
locals {
9+
10+
bucket_config = [{
11+
access_tags = var.bucket_access_tags
12+
bucket_name = "${local.prefix}${var.bucket_name}"
13+
kms_encryption_enabled = var.kms_encryption_enabled
14+
add_bucket_name_suffix = var.add_bucket_name_suffix
15+
kms_guid = local.existing_kms_instance_guid
16+
kms_key_crn = local.kms_key_crn
17+
skip_iam_authorization_policy = local.create_cross_account_auth_policy || var.skip_cos_kms_iam_auth_policy
18+
management_endpoint_type = var.management_endpoint_type_for_bucket
19+
region_location = var.region
20+
resource_instance_id = var.existing_cos_instance_crn
21+
storage_class = var.bucket_storage_class
22+
force_delete = var.force_delete
23+
hard_quota = var.bucket_hard_quota
24+
expire_filter_prefix = var.expire_filter_prefix
25+
archive_filter_prefix = var.archive_filter_prefix
26+
object_locking_enabled = var.enable_object_locking
27+
object_lock_duration_days = var.object_lock_duration_days
28+
object_lock_duration_years = var.object_lock_duration_years
29+
30+
activity_tracking = {
31+
read_data_events = true
32+
write_data_events = true
33+
}
34+
archive_rule = var.archive_days != null ? {
35+
enable = true
36+
days = var.archive_days
37+
type = var.archive_type
38+
} : null
39+
expire_rule = var.expire_days != null ? {
40+
enable = true
41+
days = var.expire_days
42+
} : null
43+
metrics_monitoring = {
44+
usage_metrics_enabled = true
45+
request_metrics_enabled = true
46+
management_events = true
47+
metrics_monitoring_crn = var.monitoring_crn
48+
}
49+
object_versioning = {
50+
enable = var.enable_object_versioning
51+
}
52+
retention_rule = var.enable_retention ? {
53+
default = var.default_retention_days
54+
maximum = var.maximum_retention_days
55+
minimum = var.minimum_retention_days
56+
permanent = var.enable_permanent_retention
57+
} : null
58+
cos_bucket_cbr_rules = var.cos_bucket_cbr_rules
59+
}]
60+
}
61+
#######################################################################################################################
62+
# Parse COS
63+
#######################################################################################################################
64+
65+
module "cos_instance_crn_parser" {
66+
source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser"
67+
version = "1.1.0"
68+
crn = var.existing_cos_instance_crn
69+
}
70+
71+
locals {
72+
cos_instance_guid = module.cos_instance_crn_parser.service_instance
73+
}
74+
#######################################################################################################################
75+
# KMS Key
76+
#######################################################################################################################
77+
78+
locals {
79+
existing_kms_instance_guid = var.kms_encryption_enabled ? var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].service_instance : var.existing_kms_key_crn != null ? module.kms_key_crn_parser[0].service_instance : null : null
80+
kms_region = var.kms_encryption_enabled ? var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].region : var.existing_kms_key_crn != null ? module.kms_key_crn_parser[0].region : null : null
81+
kms_service_name = var.kms_encryption_enabled ? var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].service_name : var.existing_kms_key_crn != null ? module.kms_key_crn_parser[0].service_name : null : null
82+
kms_account_id = var.kms_encryption_enabled ? var.existing_kms_instance_crn != null ? module.kms_instance_crn_parser[0].account_id : var.existing_kms_key_crn != null ? module.kms_key_crn_parser[0].account_id : null : null
83+
kms_key_crn = var.kms_encryption_enabled ? var.existing_kms_key_crn != null ? var.existing_kms_key_crn : module.kms[0].keys[format("%s.%s", var.cos_key_ring_name, var.cos_key_name)].crn : null
84+
kms_key_id = var.kms_encryption_enabled ? var.existing_kms_key_crn != null ? module.kms_key_crn_parser[0].resource : module.kms[0].keys[format("%s.%s", var.cos_key_ring_name, var.cos_key_name)].key_id : null
85+
create_cross_account_auth_policy = !var.skip_cos_kms_iam_auth_policy && var.ibmcloud_kms_api_key != null
86+
}
87+
########################################################################################################################
88+
# Parse KMS info from given CRNs
89+
########################################################################################################################
90+
91+
module "kms_instance_crn_parser" {
92+
count = var.existing_kms_instance_crn != null ? 1 : 0
93+
source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser"
94+
version = "1.1.0"
95+
crn = var.existing_kms_instance_crn
96+
}
97+
98+
module "kms_key_crn_parser" {
99+
count = var.existing_kms_key_crn != null ? 1 : 0
100+
source = "terraform-ibm-modules/common-utilities/ibm//modules/crn-parser"
101+
version = "1.1.0"
102+
crn = var.existing_kms_key_crn
103+
}
104+
105+
# Create IAM Authorization Policy to allow COS to access KMS for the encryption key
106+
resource "ibm_iam_authorization_policy" "cos_kms_policy" {
107+
count = local.create_cross_account_auth_policy ? 1 : 0
108+
provider = ibm.kms
109+
source_service_account = module.cos_instance_crn_parser.account_id
110+
source_service_name = "cloud-object-storage"
111+
source_resource_instance_id = local.cos_instance_guid
112+
roles = ["Reader"]
113+
description = "Allow the COS instance ${local.cos_instance_guid} to read the ${local.kms_service_name} key ${local.kms_key_id} from the instance ${local.existing_kms_instance_guid}"
114+
resource_attributes {
115+
name = "serviceName"
116+
operator = "stringEquals"
117+
value = local.kms_service_name
118+
}
119+
resource_attributes {
120+
name = "accountId"
121+
operator = "stringEquals"
122+
value = local.kms_account_id
123+
}
124+
resource_attributes {
125+
name = "serviceInstance"
126+
operator = "stringEquals"
127+
value = local.existing_kms_instance_guid
128+
}
129+
resource_attributes {
130+
name = "resourceType"
131+
operator = "stringEquals"
132+
value = "key"
133+
}
134+
resource_attributes {
135+
name = "resource"
136+
operator = "stringEquals"
137+
value = local.kms_key_id
138+
}
139+
# Scope of policy now includes the key, so ensure to create new policy before
140+
# destroying old one to prevent any disruption to every day services.
141+
lifecycle {
142+
create_before_destroy = true
143+
}
144+
}
145+
146+
resource "time_sleep" "wait_for_authorization_policy" {
147+
depends_on = [ibm_iam_authorization_policy.cos_kms_policy]
148+
create_duration = "30s"
149+
}
150+
151+
# KMS root key for COS bucket
152+
module "kms" {
153+
providers = {
154+
ibm = ibm.kms
155+
}
156+
count = var.kms_encryption_enabled && var.existing_kms_key_crn != null ? 0 : 1 # no need to create any KMS resources if passing an existing key.
157+
source = "terraform-ibm-modules/kms-all-inclusive/ibm"
158+
version = "5.1.7"
159+
create_key_protect_instance = false
160+
region = local.kms_region
161+
existing_kms_instance_crn = var.existing_kms_instance_crn
162+
key_ring_endpoint_type = var.kms_endpoint_type
163+
key_endpoint_type = var.kms_endpoint_type
164+
keys = [
165+
{
166+
key_ring_name = var.cos_key_ring_name
167+
existing_key_ring = false
168+
keys = [
169+
{
170+
key_name = var.cos_key_name
171+
standard_key = false
172+
rotation_interval_month = 3
173+
dual_auth_delete_enabled = false
174+
force_delete = true
175+
}
176+
]
177+
}
178+
]
179+
}
180+
181+
#######################################################################################################################
182+
# COS Bucket
183+
#######################################################################################################################
184+
185+
module "cos" {
186+
providers = {
187+
ibm = ibm.cos
188+
}
189+
depends_on = [time_sleep.wait_for_authorization_policy]
190+
source = "../../../modules/buckets"
191+
bucket_configs = local.bucket_config
192+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
##############################################################################
2+
# Outputs
3+
##############################################################################
4+
output "buckets" {
5+
description = "The list of buckets created by this DA."
6+
value = module.cos.buckets
7+
}
8+
9+
output "s3_endpoint_direct" {
10+
description = "The s3 direct endpoint of the created bucket."
11+
value = try(module.cos.buckets[var.bucket_name].s3_endpoint_direct, null)
12+
}
13+
14+
output "s3_endpoint_private" {
15+
description = "The s3 private endpoint of the created bucket."
16+
value = try(module.cos.buckets[var.bucket_name].s3_endpoint_private, null)
17+
}
18+
19+
output "bucket_name" {
20+
description = "The name of the bucket that was created. Includes the optional suffix if enabled."
21+
value = try(module.cos.buckets[var.bucket_name].bucket_name, null)
22+
}
23+
24+
output "cos_instance_crn" {
25+
description = "The CRN of the COS instance containing the created bucket."
26+
value = var.existing_cos_instance_crn
27+
}
28+
29+
output "cos_instance_guid" {
30+
description = "The guid of the COS instance containing the created bucket."
31+
value = local.cos_instance_guid
32+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
provider "ibm" {
2+
alias = "cos"
3+
ibmcloud_api_key = var.ibmcloud_api_key
4+
visibility = var.provider_visibility
5+
private_endpoint_type = (var.provider_visibility == "private" && var.region == "ca-mon") ? "vpe" : null
6+
}
7+
8+
provider "ibm" {
9+
alias = "kms"
10+
ibmcloud_api_key = var.ibmcloud_kms_api_key != null ? var.ibmcloud_kms_api_key : var.ibmcloud_api_key
11+
region = local.kms_region
12+
visibility = var.provider_visibility
13+
private_endpoint_type = (var.provider_visibility == "private" && var.region == "ca-mon") ? "vpe" : null
14+
}

0 commit comments

Comments
 (0)