Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: move deprecated expiry & archive blocks to new resource #769

Open
wants to merge 36 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
05d10da
feat: move deprecated expiry & archive blocks to new resource
Nov 4, 2024
36c2c84
feat: move deprecated expiry & archive blocks to new resource
Nov 4, 2024
04e7617
feat: move deprecated expiry & archive blocks to new resource
Nov 4, 2024
b0dd177
feat: move deprecated expiry & archive blocks to new resource
Nov 4, 2024
4d1ea68
feat: move deprecated expiry & archive blocks to new resource
Nov 4, 2024
3da5c91
Merge branch 'main' into migration
jor2 Nov 4, 2024
e7621df
feat: move deprecated expiry & archive blocks to new resource
Nov 4, 2024
c75b669
feat: move deprecated expiry & archive blocks to new resource
Nov 4, 2024
5c39ff4
feat: move deprecated expiry & archive blocks to new resource
Nov 4, 2024
b9954a8
feat: move deprecated expiry & archive blocks to new resource
Nov 4, 2024
25933b5
feat: move deprecated expiry & archive blocks to new resource
Nov 4, 2024
90dfadb
feat: move deprecated expiry & archive blocks to new resource
Nov 4, 2024
9213537
feat: move deprecated expiry & archive blocks to new resource
Nov 4, 2024
8c1d7ab
feat: move deprecated expiry & archive blocks to new resource
Nov 4, 2024
f073b0d
feat: move deprecated expiry & archive blocks to new resource
Nov 4, 2024
674b090
fix: add create sleep SKIP UPGRADE TEST
Nov 4, 2024
a5a9a92
fix: add create sleep SKIP UPGRADE TEST
Nov 4, 2024
4d4b23e
fix: address comments
Nov 6, 2024
77e59c4
fix: address comments
Nov 6, 2024
cad3f74
fix: address comments
Nov 6, 2024
d690fbc
fix: address comments
Nov 6, 2024
1592b93
fix: address comments
Nov 7, 2024
7c74554
feat: add vars to DA and fscloud
Nov 8, 2024
102a761
fix: add issue comment
Nov 8, 2024
5e7e378
Merge branch 'main' into migration
jor2 Nov 10, 2024
7fb2281
Merge branch 'main' into migration
jor2 Nov 14, 2024
95ec0a6
Merge branch 'main' into migration
jor2 Nov 20, 2024
0b65c41
fix: add var
Nov 20, 2024
9dca820
fix: add var
Nov 20, 2024
b7d5bf7
fix: add var
Nov 20, 2024
6e8c82a
Merge branch 'main' into migration
jor2 Nov 25, 2024
9882767
Merge branch 'main' into migration
jor2 Nov 27, 2024
96dd747
Merge branch 'main' into migration
jor2 Nov 28, 2024
a77235a
Merge branch 'main' into migration
jor2 Dec 2, 2024
c256bf1
Merge branch 'main' into migration
jor2 Dec 12, 2024
2b48d17
Merge branch 'main' into migration
ocofaigh Dec 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"files": "go.sum|^.secrets.baseline$",
"lines": null
},
"generated_at": "2024-10-30T15:05:28Z",
"generated_at": "2024-11-07T00:11:34Z",
"plugins_used": [
{
"name": "AWSKeyDetector"
Expand Down Expand Up @@ -90,7 +90,7 @@
"hashed_secret": "a7c93faaa770c377154ea9d4d0d17a9056dbfa95",
"is_secret": false,
"is_verified": false,
"line_number": 195,
"line_number": 199,
"type": "Secret Keyword",
"verified_result": null
}
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,15 @@ You need the following permissions to run this module.
|------|------|
| [ibm_cos_bucket.cos_bucket](https://registry.terraform.io/providers/ibm-cloud/ibm/latest/docs/resources/cos_bucket) | resource |
| [ibm_cos_bucket.cos_bucket1](https://registry.terraform.io/providers/ibm-cloud/ibm/latest/docs/resources/cos_bucket) | resource |
| [ibm_cos_bucket_lifecycle_configuration.cos_bucket_lifecycle](https://registry.terraform.io/providers/ibm-cloud/ibm/latest/docs/resources/cos_bucket_lifecycle_configuration) | resource |
| [ibm_cos_bucket_object_lock_configuration.lock_configuration](https://registry.terraform.io/providers/ibm-cloud/ibm/latest/docs/resources/cos_bucket_object_lock_configuration) | resource |
| [ibm_iam_authorization_policy.policy](https://registry.terraform.io/providers/ibm-cloud/ibm/latest/docs/resources/iam_authorization_policy) | resource |
| [ibm_resource_instance.cos_instance](https://registry.terraform.io/providers/ibm-cloud/ibm/latest/docs/resources/resource_instance) | resource |
| [ibm_resource_key.resource_keys](https://registry.terraform.io/providers/ibm-cloud/ibm/latest/docs/resources/resource_key) | resource |
| [ibm_resource_tag.cos_access_tag](https://registry.terraform.io/providers/ibm-cloud/ibm/latest/docs/resources/resource_tag) | resource |
| [random_string.bucket_name_suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource |
| [time_sleep.wait_for_authorization_policy](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource |
| [time_sleep.wait_for_cos_bucket_lifecycle](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource |

### Inputs

Expand All @@ -164,6 +166,7 @@ You need the following permissions to run this module.
| <a name="input_activity_tracker_write_data_events"></a> [activity\_tracker\_write\_data\_events](#input\_activity\_tracker\_write\_data\_events) | If set to true, all Object Storage bucket write events (i.e. uploads) will be sent to Activity Tracker. | `bool` | `true` | no |
| <a name="input_add_bucket_name_suffix"></a> [add\_bucket\_name\_suffix](#input\_add\_bucket\_name\_suffix) | Whether to add a randomly generated 4-character suffix to the new bucket name. | `bool` | `false` | no |
| <a name="input_archive_days"></a> [archive\_days](#input\_archive\_days) | The number of days before the `archive_type` rule action takes effect. Applies only if `create_cos_bucket` is true. Set to `null` if you specify a bucket location in `cross_region_location` because archive data is not supported with cross-region buckets. | `number` | `90` | no |
| <a name="input_archive_filter_prefix"></a> [archive\_filter\_prefix](#input\_archive\_filter\_prefix) | Apply archive lifecycle rule to only objects with the following prefix. Defaults to apply to all objects. | `string` | `null` | no |
| <a name="input_archive_type"></a> [archive\_type](#input\_archive\_type) | The storage class or archive type to which you want the object to transition. Possible values: `Glacier`, `Accelerated`. Applies only if `create_cos_bucket` is true. | `string` | `"Glacier"` | no |
| <a name="input_bucket_cbr_rules"></a> [bucket\_cbr\_rules](#input\_bucket\_cbr\_rules) | The list of context-based restriction rules to create for the bucket. | <pre>list(object({<br/> description = string<br/> account_id = string<br/> rule_contexts = list(object({<br/> attributes = optional(list(object({<br/> name = string<br/> value = string<br/> }))) }))<br/> enforcement_mode = string<br/> tags = optional(list(object({<br/> name = string<br/> value = string<br/> })), [])<br/> operations = optional(list(object({<br/> api_types = list(object({<br/> api_type_id = string<br/> }))<br/> })))<br/> }))</pre> | `[]` | no |
| <a name="input_bucket_name"></a> [bucket\_name](#input\_bucket\_name) | The name for the new Object Storage bucket. Applies only if `create_cos_bucket` is true. | `string` | `null` | no |
Expand All @@ -178,6 +181,7 @@ You need the following permissions to run this module.
| <a name="input_existing_cos_instance_id"></a> [existing\_cos\_instance\_id](#input\_existing\_cos\_instance\_id) | The ID of an existing cloud object storage instance. Required if `create_cos_instance` is false. | `string` | `null` | no |
| <a name="input_existing_kms_instance_guid"></a> [existing\_kms\_instance\_guid](#input\_existing\_kms\_instance\_guid) | The GUID of the Key Protect or Hyper Protect Crypto Services instance that holds the key specified in `kms_key_crn`. Required if `skip_iam_authorization_policy` is false. | `string` | `null` | no |
| <a name="input_expire_days"></a> [expire\_days](#input\_expire\_days) | The number of days before the expire rule action takes effect. Applies only if `create_cos_bucket` is true. | `number` | `365` | no |
| <a name="input_expire_filter_prefix"></a> [expire\_filter\_prefix](#input\_expire\_filter\_prefix) | Apply expire lifecycle rule to only objects with the following prefix. Defaults to apply to all objects. | `string` | `null` | no |
| <a name="input_force_delete"></a> [force\_delete](#input\_force\_delete) | Whether to delete all the objects in the Object Storage bucket before the bucket is deleted. | `bool` | `true` | no |
| <a name="input_hard_quota"></a> [hard\_quota](#input\_hard\_quota) | The maximum amount of available storage in bytes for a bucket. If set to `null`, the quota is disabled. | `number` | `null` | no |
| <a name="input_instance_cbr_rules"></a> [instance\_cbr\_rules](#input\_instance\_cbr\_rules) | The list of context-based restriction rules to create for the instance. | <pre>list(object({<br/> description = string<br/> account_id = string<br/> rule_contexts = list(object({<br/> attributes = optional(list(object({<br/> name = string<br/> value = string<br/> }))) }))<br/> enforcement_mode = string<br/> tags = optional(list(object({<br/> name = string<br/> value = string<br/> })), [])<br/> operations = optional(list(object({<br/> api_types = list(object({<br/> api_type_id = string<br/> }))<br/> })))<br/> }))</pre> | `[]` | no |
Expand Down
100 changes: 62 additions & 38 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -182,25 +182,6 @@ resource "ibm_cos_bucket" "cos_bucket" {
permanent = var.retention_permanent
}
}
## This for_each block is NOT a loop to attach to multiple archive blocks.
## This block is only used to conditionally add retention block depending on archive rule is enabled.
dynamic "archive_rule" {
for_each = local.archive_enabled
content {
enable = true
days = var.archive_days
type = var.archive_type
}
}
## This for_each block is NOT a loop to attach to multiple expire blocks.
## This block is only used to conditionally add retention block depending on expire rule is enabled.
dynamic "expire_rule" {
for_each = local.expire_enabled
content {
enable = true
days = var.expire_days
}
}
## This for_each block is NOT a loop to attach to multiple Activity Tracker instances.
## This block is only used to conditionally attach activity tracker depending on AT CRN is provided.
dynamic "activity_tracking" {
Expand Down Expand Up @@ -260,25 +241,6 @@ resource "ibm_cos_bucket" "cos_bucket1" {
permanent = var.retention_permanent
}
}
## This for_each block is NOT a loop to attach to multiple archive blocks.
## This block is only used to conditionally add retention block depending on archive rule is enabled.
dynamic "archive_rule" {
for_each = local.archive_enabled
content {
enable = true
days = var.archive_days
type = var.archive_type
}
}
## This for_each block is NOT a loop to attach to multiple Activity Tracker instances.
## This block is only used to conditionally attach activity tracker depending on AT CRN is provided.
dynamic "expire_rule" {
for_each = local.expire_enabled
content {
enable = true
days = var.expire_days
}
}
## This for_each block is NOT a loop to attach to multiple Activity Tracker instances.
## This block is only used to conditionally attach activity tracker depending on AT CRN is provided.
dynamic "activity_tracking" {
Expand Down Expand Up @@ -307,6 +269,68 @@ resource "ibm_cos_bucket" "cos_bucket1" {
}
}

locals {
expiration_or_archiving_rule_enabled = (length(local.expire_enabled) != 0 || length(local.archive_enabled) != 0)

create_cos_bucket = (var.kms_encryption_enabled && var.create_cos_bucket) ? true : false
create_cos_bucket1 = (!var.kms_encryption_enabled && var.create_cos_bucket) ? true : false

cos_bucket_resource = local.create_cos_bucket ? ibm_cos_bucket.cos_bucket : local.create_cos_bucket1 ? ibm_cos_bucket.cos_bucket1 : null

## Only one of these values can be set, leaving 2 of 3 null, compact function removes nulls.
## We then take the only value left in the list
cos_region = compact([var.region, var.cross_region_location, var.single_site_location])[0]
}

resource "time_sleep" "wait_for_cos_bucket_lifecycle" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need a sleep? Is there a timing issue? What error do you get? If so have we created a provider bug for it? If the ibm_cos_bucket has returned as complete, I expect it to be ready for lifecycle configuration. Otherwise I think its a bug. Please leave code comments when adding workaround like this explaining why they were added (and link to any associated issues)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is a timing issue because after the first apply unless I waited a couple seconds to reapply it would fail saying it couldn't find the lifecycle policy. I will create a provider issue and link in a comment above sleep.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestRunAdvancedExample 2024-11-06T23:58:29Z command.go:185: Planning failed. Terraform encountered an error while generating this plan.
TestRunAdvancedExample 2024-11-06T23:58:29Z command.go:185: 
TestRunAdvancedExample 2024-11-06T23:58:29Z command.go:185: ╷
Error: AdvancedExample 2024-11-06T23:58:29Z command.go:185: │ Error: [ERROR] Error getting Lifecycle Configuration for the bucket cos-advanced-91sqap-bucket-3-3hva
TestRunAdvancedExample 2024-11-06T23:58:29Z command.go:185: │ 
TestRunAdvancedExample 2024-11-06T23:58:29Z command.go:185: │   with module.cos_bucket3.ibm_cos_bucket_lifecycle_configuration.cos_bucket_lifecycle[0],
TestRunAdvancedExample 2024-11-06T23:58:29Z command.go:185: │   on ../../main.tf line 285, in resource "ibm_cos_bucket_lifecycle_configuration" "cos_bucket_lifecycle":
TestRunAdvancedExample 2024-11-06T23:58:29Z command.go:185:285: resource "ibm_cos_bucket_lifecycle_configuration" "cos_bucket_lifecycle" {

The error can be seen here in this run when I removed the sleep. It doesn't occur every time, but often enough.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as per internal discussions, the issue happens on the plan AFTER apply has completed. Therefor it seems a bug with the ibm_cos_bucket_lifecycle_configuration resource. If its not fully ready, it should of been marked as complete by terraform. Lets create a provider issue, and hold off fix this migration until its fixed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

count = (local.create_cos_bucket || local.create_cos_bucket1) && local.expiration_or_archiving_rule_enabled ? 1 : 0
# workaround for https://github.com/IBM-Cloud/terraform-provider-ibm/issues/5778
create_duration = "90s"
}

resource "ibm_cos_bucket_lifecycle_configuration" "cos_bucket_lifecycle" {
count = (local.create_cos_bucket || local.create_cos_bucket1) && local.expiration_or_archiving_rule_enabled ? 1 : 0

depends_on = [time_sleep.wait_for_cos_bucket_lifecycle]

bucket_crn = local.cos_bucket_resource[count.index].crn
bucket_location = local.cos_region

dynamic "lifecycle_rule" {
## This for_each block is NOT a loop to attach to multiple expiration blocks.
## This block is only used to conditionally add expiration block depending on expire rule is enabled.
for_each = local.expire_enabled
content {
expiration {
days = var.expire_days
}
filter {
prefix = var.expire_filter_prefix != null ? var.expire_filter_prefix : ""
}
rule_id = "expiry-rule"
status = "enable"
}
}
dynamic "lifecycle_rule" {
## This for_each block is NOT a loop to attach to multiple transition blocks.
## This block is only used to conditionally add retention block depending on archive rule is enabled.
for_each = local.archive_enabled
content {
transition {
days = var.archive_days
## The new values changed from Capatalized to all Upper case, avoid having to change values in new release
storage_class = upper(var.archive_type)

}
filter {
prefix = var.archive_filter_prefix != null ? var.archive_filter_prefix : ""
}
rule_id = "archive-rule"
status = "enable"
}
}
}

locals {
bucket_crn = var.create_cos_bucket ? (var.kms_encryption_enabled ? ibm_cos_bucket.cos_bucket[0].crn : ibm_cos_bucket.cos_bucket1[0].crn) : null
bucket_id = var.create_cos_bucket ? (var.kms_encryption_enabled ? ibm_cos_bucket.cos_bucket[0].id : ibm_cos_bucket.cos_bucket1[0].id) : null
Expand Down
2 changes: 1 addition & 1 deletion modules/buckets/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ You need the following permissions to run this module.

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_bucket_configs"></a> [bucket\_configs](#input\_bucket\_configs) | The Object Storage bucket configurations. | <pre>list(object({<br/> access_tags = optional(list(string), [])<br/> add_bucket_name_suffix = optional(bool, false)<br/> bucket_name = string<br/> kms_encryption_enabled = optional(bool, true)<br/> kms_guid = optional(string, null)<br/> kms_key_crn = optional(string, null)<br/> skip_iam_authorization_policy = optional(bool, false)<br/> management_endpoint_type = optional(string, "public")<br/> cross_region_location = optional(string, null)<br/> storage_class = optional(string, "smart")<br/> region_location = optional(string, null)<br/> resource_instance_id = string<br/> force_delete = optional(bool, true)<br/> single_site_location = optional(string, null)<br/> hard_quota = optional(number, null)<br/> object_locking_enabled = optional(bool, false)<br/> object_lock_duration_days = optional(number, 0)<br/> object_lock_duration_years = optional(number, 0)<br/><br/> activity_tracking = optional(object({<br/> read_data_events = optional(bool, true)<br/> write_data_events = optional(bool, true)<br/> management_events = optional(bool, true)<br/> }))<br/> archive_rule = optional(object({<br/> enable = optional(bool, false)<br/> days = optional(number, 20)<br/> type = optional(string, "Glacier")<br/> }))<br/> expire_rule = optional(object({<br/> enable = optional(bool, false)<br/> days = optional(number, 365)<br/> }))<br/> metrics_monitoring = optional(object({<br/> usage_metrics_enabled = optional(bool, true)<br/> request_metrics_enabled = optional(bool, true)<br/> metrics_monitoring_crn = optional(string, null)<br/> }))<br/> object_versioning = optional(object({<br/> enable = optional(bool, false)<br/> }))<br/> retention_rule = optional(object({<br/> default = optional(number, 90)<br/> maximum = optional(number, 350)<br/> minimum = optional(number, 90)<br/> permanent = optional(bool, false)<br/> }))<br/> cbr_rules = optional(list(object({<br/> description = string<br/> account_id = string<br/> rule_contexts = list(object({<br/> attributes = optional(list(object({<br/> name = string<br/> value = string<br/> }))) }))<br/> enforcement_mode = string<br/> tags = optional(list(object({<br/> name = string<br/> value = string<br/> })), [])<br/> operations = optional(list(object({<br/> api_types = list(object({<br/> api_type_id = string<br/> }))<br/> })))<br/> })), [])<br/><br/> }))</pre> | n/a | yes |
| <a name="input_bucket_configs"></a> [bucket\_configs](#input\_bucket\_configs) | The Object Storage bucket configurations. | <pre>list(object({<br/> access_tags = optional(list(string), [])<br/> add_bucket_name_suffix = optional(bool, false)<br/> bucket_name = string<br/> kms_encryption_enabled = optional(bool, true)<br/> kms_guid = optional(string, null)<br/> kms_key_crn = optional(string, null)<br/> skip_iam_authorization_policy = optional(bool, false)<br/> management_endpoint_type = optional(string, "public")<br/> cross_region_location = optional(string, null)<br/> storage_class = optional(string, "smart")<br/> region_location = optional(string, null)<br/> resource_instance_id = string<br/> force_delete = optional(bool, true)<br/> single_site_location = optional(string, null)<br/> hard_quota = optional(number, null)<br/> expire_filter_prefix = optional(string, null)<br/> archive_filter_prefix = optional(string, null)<br/> object_locking_enabled = optional(bool, false)<br/> object_lock_duration_days = optional(number, 0)<br/> object_lock_duration_years = optional(number, 0)<br/><br/> activity_tracking = optional(object({<br/> read_data_events = optional(bool, true)<br/> write_data_events = optional(bool, true)<br/> management_events = optional(bool, true)<br/> }))<br/> archive_rule = optional(object({<br/> enable = optional(bool, false)<br/> days = optional(number, 20)<br/> type = optional(string, "Glacier")<br/> }))<br/> expire_rule = optional(object({<br/> enable = optional(bool, false)<br/> days = optional(number, 365)<br/> }))<br/> metrics_monitoring = optional(object({<br/> usage_metrics_enabled = optional(bool, true)<br/> request_metrics_enabled = optional(bool, true)<br/> metrics_monitoring_crn = optional(string, null)<br/> }))<br/> object_versioning = optional(object({<br/> enable = optional(bool, false)<br/> }))<br/> retention_rule = optional(object({<br/> default = optional(number, 90)<br/> maximum = optional(number, 350)<br/> minimum = optional(number, 90)<br/> permanent = optional(bool, false)<br/> }))<br/> cbr_rules = optional(list(object({<br/> description = string<br/> account_id = string<br/> rule_contexts = list(object({<br/> attributes = optional(list(object({<br/> name = string<br/> value = string<br/> }))) }))<br/> enforcement_mode = string<br/> tags = optional(list(object({<br/> name = string<br/> value = string<br/> })), [])<br/> operations = optional(list(object({<br/> api_types = list(object({<br/> api_type_id = string<br/> }))<br/> })))<br/> })), [])<br/><br/> }))</pre> | n/a | yes |

### Outputs

Expand Down
2 changes: 2 additions & 0 deletions modules/buckets/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ module "buckets" {
management_endpoint_type_for_bucket = each.value.management_endpoint_type
force_delete = each.value.force_delete
hard_quota = each.value.hard_quota
expire_filter_prefix = each.value.expire_filter_prefix
archive_filter_prefix = each.value.archive_filter_prefix
object_locking_enabled = each.value.object_locking_enabled
object_lock_duration_days = each.value.object_lock_duration_days
object_lock_duration_years = each.value.object_lock_duration_years
Expand Down
2 changes: 2 additions & 0 deletions modules/buckets/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ variable "bucket_configs" {
force_delete = optional(bool, true)
single_site_location = optional(string, null)
hard_quota = optional(number, null)
expire_filter_prefix = optional(string, null)
archive_filter_prefix = optional(string, null)
object_locking_enabled = optional(bool, false)
object_lock_duration_days = optional(number, 0)
object_lock_duration_years = optional(number, 0)
Expand Down
Loading