From 44e9b9fc41d3a143456803cc4a32b4561c65dcde Mon Sep 17 00:00:00 2001 From: Michael Scribellito Date: Mon, 8 Apr 2024 13:55:14 -0400 Subject: [PATCH 1/2] add cloud storage --- modules/storage/README.md | 3 ++ modules/storage/main.tf | 80 +++++++++++++++++++++++++++++ modules/storage/outputs.tf | 29 +++++++++++ modules/storage/variables.tf | 97 ++++++++++++++++++++++++++++++++++++ 4 files changed, 209 insertions(+) create mode 100644 modules/storage/README.md create mode 100644 modules/storage/main.tf create mode 100644 modules/storage/outputs.tf create mode 100644 modules/storage/variables.tf diff --git a/modules/storage/README.md b/modules/storage/README.md new file mode 100644 index 0000000..fd7ea0c --- /dev/null +++ b/modules/storage/README.md @@ -0,0 +1,3 @@ +# Google Cloud Log Export Storage Destination Terraform Module + +This module allows you to configure a Storage bucket destination that can be used by the log export module. \ No newline at end of file diff --git a/modules/storage/main.tf b/modules/storage/main.tf new file mode 100644 index 0000000..f2a5204 --- /dev/null +++ b/modules/storage/main.tf @@ -0,0 +1,80 @@ +locals { + storage_bucket_name = element(concat(google_storage_bucket.bucket[*].name, [""]), 0) + destination_uri = "storage.googleapis.com/${local.storage_bucket_name}" +} + +#----------------# +# API activation # +#----------------# +resource "google_project_service" "enable_destination_api" { + project = var.project_id + service = "storage-component.googleapis.com" + disable_on_destroy = false + disable_dependent_services = false +} + +#----------------# +# Storage bucket # +#----------------# +resource "google_storage_bucket" "bucket" { + name = var.storage_bucket_name + project = google_project_service.enable_destination_api.project + storage_class = var.storage_class + location = var.location + force_destroy = var.force_destroy + uniform_bucket_level_access = var.uniform_bucket_level_access + labels = var.storage_bucket_labels + public_access_prevention = var.public_access_prevention + versioning { + enabled = var.versioning + } + + dynamic "lifecycle_rule" { + for_each = var.lifecycle_rules + content { + action { + type = lifecycle_rule.value.action.type + storage_class = lookup(lifecycle_rule.value.action, "storage_class", null) + } + condition { + age = lookup(lifecycle_rule.value.condition, "age", null) + created_before = lookup(lifecycle_rule.value.condition, "created_before", null) + with_state = lookup(lifecycle_rule.value.condition, "with_state", lookup(lifecycle_rule.value.condition, "is_live", false) ? "LIVE" : null) + matches_storage_class = contains(keys(lifecycle_rule.value.condition), "matches_storage_class") ? split(",", lifecycle_rule.value.condition["matches_storage_class"]) : null + num_newer_versions = lookup(lifecycle_rule.value.condition, "num_newer_versions", null) + days_since_custom_time = lookup(lifecycle_rule.value.condition, "days_since_custom_time", null) + } + } + } + + dynamic "retention_policy" { + for_each = var.retention_policy == null ? [] : [var.retention_policy] + content { + is_locked = var.retention_policy.is_locked + retention_period = var.retention_policy.retention_period_days * 24 * 60 * 60 // days to seconds + } + } + + dynamic "encryption" { + for_each = var.kms_key_name == null ? [] : [var.kms_key_name] + content { + default_kms_key_name = var.kms_key_name + } + } + + dynamic "custom_placement_config" { + for_each = var.data_locations == null ? [] : [var.data_locations] + content { + data_locations = var.data_locations + } + } +} + +#--------------------------------# +# Service account IAM membership # +#--------------------------------# +resource "google_storage_bucket_iam_member" "storage_sink_member" { + bucket = local.storage_bucket_name + role = "roles/storage.objectCreator" + member = var.log_sink_writer_identity +} diff --git a/modules/storage/outputs.tf b/modules/storage/outputs.tf new file mode 100644 index 0000000..f930295 --- /dev/null +++ b/modules/storage/outputs.tf @@ -0,0 +1,29 @@ +output "console_link" { + description = "The console link to the destination storage bucket" + value = "https://console.cloud.google.com/storage/browser/${local.storage_bucket_name}?project=${var.project_id}" +} + +output "project" { + description = "The project in which the storage bucket was created." + value = google_storage_bucket.bucket.project +} + +output "resource_name" { + description = "The resource name for the destination storage bucket" + value = local.storage_bucket_name +} + +output "resource_id" { + description = "The resource id for the destination storage bucket" + value = google_storage_bucket.bucket.id +} + +output "self_link" { + description = "The self_link URI for the destination storage bucket" + value = google_storage_bucket.bucket.self_link +} + +output "destination_uri" { + description = "The destination URI for the storage bucket." + value = local.destination_uri +} diff --git a/modules/storage/variables.tf b/modules/storage/variables.tf new file mode 100644 index 0000000..a0d519b --- /dev/null +++ b/modules/storage/variables.tf @@ -0,0 +1,97 @@ +variable "log_sink_writer_identity" { + description = "The service account that logging uses to write log entries to the destination. (This is available as an output coming from the root module)." + type = string +} + +variable "project_id" { + description = "The ID of the project in which the storage bucket will be created." + type = string +} + +variable "storage_bucket_name" { + description = "The name of the storage bucket to be created and used for log entries matching the filter." + type = string +} + +variable "location" { + description = "The location of the storage bucket." + type = string + default = "US" +} + +variable "storage_class" { + description = "The storage class of the storage bucket." + type = string + default = "STANDARD" +} + +variable "storage_bucket_labels" { + description = "Labels to apply to the storage bucket." + type = map(string) + default = {} +} + +variable "uniform_bucket_level_access" { + description = "Enables Uniform bucket-level access to a bucket." + type = bool + default = true +} + +variable "lifecycle_rules" { + type = set(object({ + # Object with keys: + # - type - The type of the action of this Lifecycle Rule. Supported values: Delete and SetStorageClass. + # - storage_class - (Required if action type is SetStorageClass) The target Storage Class of objects affected by this Lifecycle Rule. + action = map(string) + + # Object with keys: + # - age - (Optional) Minimum age of an object in days to satisfy this condition. + # - created_before - (Optional) Creation date of an object in RFC 3339 (e.g. 2017-06-13) to satisfy this condition. + # - with_state - (Optional) Match to live and/or archived objects. Supported values include: "LIVE", "ARCHIVED", "ANY". + # - matches_storage_class - (Optional) Comma delimited string for storage class of objects to satisfy this condition. Supported values include: MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, STANDARD, DURABLE_REDUCED_AVAILABILITY. + # - num_newer_versions - (Optional) Relevant only for versioned objects. The number of newer versions of an object to satisfy this condition. + # - days_since_custom_time - (Optional) The number of days from the Custom-Time metadata attribute after which this condition becomes true. + condition = map(string) + })) + description = "List of lifecycle rules to configure. Format is the same as described in provider documentation https://www.terraform.io/docs/providers/google/r/storage_bucket.html#lifecycle_rule except condition.matches_storage_class should be a comma delimited string." + default = [] +} + +variable "force_destroy" { + description = "When deleting a bucket, this boolean option will delete all contained objects." + type = bool + default = false +} + +variable "retention_policy" { + description = "Configuration of the bucket's data retention policy for how long objects in the bucket should be retained." + type = object({ + is_locked = bool + retention_period_days = number + }) + default = null +} + +variable "versioning" { + description = "Toggles bucket versioning, ability to retain a non-current object version when the live object version gets replaced or deleted." + type = bool + default = false +} + +variable "kms_key_name" { + description = "ID of a Cloud KMS key that will be used to encrypt objects inserted into this bucket. Automatic Google Cloud Storage service account for the bucket's project requires access to this encryption key." + type = string + default = null +} + +variable "data_locations" { + description = "Configuration of the bucket's custom location in a dual-region bucket setup. If the bucket is designated a single or multi-region, then the variable will be null. Note: If any of the data_locations changes, it will recreate the bucket." + type = list(string) + default = null +} + +variable "public_access_prevention" { + description = "Prevents public access to a bucket. Acceptable values are \"inherited\" or \"enforced\". If \"inherited\", the bucket uses public access prevention. only if the bucket is subject to the public access prevention organization policy constraint." + type = string + default = "inherited" +} From 6ceec736aa879b47b5e92a0776208aba76c5e45f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 8 Apr 2024 18:11:06 +0000 Subject: [PATCH 2/2] terraform-docs: automated action --- modules/storage/README.md | 55 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/modules/storage/README.md b/modules/storage/README.md index fd7ea0c..da10f7b 100644 --- a/modules/storage/README.md +++ b/modules/storage/README.md @@ -1,3 +1,56 @@ # Google Cloud Log Export Storage Destination Terraform Module -This module allows you to configure a Storage bucket destination that can be used by the log export module. \ No newline at end of file +This module allows you to configure a Storage bucket destination that can be used by the log export module. + +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| [google](#provider\_google) | n/a | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [google_project_service.enable_destination_api](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/project_service) | resource | +| [google_storage_bucket.bucket](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket) | resource | +| [google_storage_bucket_iam_member.storage_sink_member](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket_iam_member) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [data\_locations](#input\_data\_locations) | Configuration of the bucket's custom location in a dual-region bucket setup. If the bucket is designated a single or multi-region, then the variable will be null. Note: If any of the data\_locations changes, it will recreate the bucket. | `list(string)` | `null` | no | +| [force\_destroy](#input\_force\_destroy) | When deleting a bucket, this boolean option will delete all contained objects. | `bool` | `false` | no | +| [kms\_key\_name](#input\_kms\_key\_name) | ID of a Cloud KMS key that will be used to encrypt objects inserted into this bucket. Automatic Google Cloud Storage service account for the bucket's project requires access to this encryption key. | `string` | `null` | no | +| [lifecycle\_rules](#input\_lifecycle\_rules) | List of lifecycle rules to configure. Format is the same as described in provider documentation https://www.terraform.io/docs/providers/google/r/storage_bucket.html#lifecycle_rule except condition.matches\_storage\_class should be a comma delimited string. |
set(object({
# Object with keys:
# - type - The type of the action of this Lifecycle Rule. Supported values: Delete and SetStorageClass.
# - storage_class - (Required if action type is SetStorageClass) The target Storage Class of objects affected by this Lifecycle Rule.
action = map(string)

# Object with keys:
# - age - (Optional) Minimum age of an object in days to satisfy this condition.
# - created_before - (Optional) Creation date of an object in RFC 3339 (e.g. 2017-06-13) to satisfy this condition.
# - with_state - (Optional) Match to live and/or archived objects. Supported values include: "LIVE", "ARCHIVED", "ANY".
# - matches_storage_class - (Optional) Comma delimited string for storage class of objects to satisfy this condition. Supported values include: MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, STANDARD, DURABLE_REDUCED_AVAILABILITY.
# - num_newer_versions - (Optional) Relevant only for versioned objects. The number of newer versions of an object to satisfy this condition.
# - days_since_custom_time - (Optional) The number of days from the Custom-Time metadata attribute after which this condition becomes true.
condition = map(string)
}))
| `[]` | no | +| [location](#input\_location) | The location of the storage bucket. | `string` | `"US"` | no | +| [log\_sink\_writer\_identity](#input\_log\_sink\_writer\_identity) | The service account that logging uses to write log entries to the destination. (This is available as an output coming from the root module). | `string` | n/a | yes | +| [project\_id](#input\_project\_id) | The ID of the project in which the storage bucket will be created. | `string` | n/a | yes | +| [public\_access\_prevention](#input\_public\_access\_prevention) | Prevents public access to a bucket. Acceptable values are "inherited" or "enforced". If "inherited", the bucket uses public access prevention. only if the bucket is subject to the public access prevention organization policy constraint. | `string` | `"inherited"` | no | +| [retention\_policy](#input\_retention\_policy) | Configuration of the bucket's data retention policy for how long objects in the bucket should be retained. |
object({
is_locked = bool
retention_period_days = number
})
| `null` | no | +| [storage\_bucket\_labels](#input\_storage\_bucket\_labels) | Labels to apply to the storage bucket. | `map(string)` | `{}` | no | +| [storage\_bucket\_name](#input\_storage\_bucket\_name) | The name of the storage bucket to be created and used for log entries matching the filter. | `string` | n/a | yes | +| [storage\_class](#input\_storage\_class) | The storage class of the storage bucket. | `string` | `"STANDARD"` | no | +| [uniform\_bucket\_level\_access](#input\_uniform\_bucket\_level\_access) | Enables Uniform bucket-level access to a bucket. | `bool` | `true` | no | +| [versioning](#input\_versioning) | Toggles bucket versioning, ability to retain a non-current object version when the live object version gets replaced or deleted. | `bool` | `false` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [console\_link](#output\_console\_link) | The console link to the destination storage bucket | +| [destination\_uri](#output\_destination\_uri) | The destination URI for the storage bucket. | +| [project](#output\_project) | The project in which the storage bucket was created. | +| [resource\_id](#output\_resource\_id) | The resource id for the destination storage bucket | +| [resource\_name](#output\_resource\_name) | The resource name for the destination storage bucket | +| [self\_link](#output\_self\_link) | The self\_link URI for the destination storage bucket | + \ No newline at end of file