diff --git a/README.md b/README.md index 2215c2ca..dd523d34 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ Functional examples are included in the | hierarchical\_namespace | Optional map of lowercase unprefixed bucket name => boolean, defaults to false. | `map(bool)` | `{}` | no | | hmac\_key\_admins | IAM-style members who will be granted roles/storage.hmacKeyAdmin on all buckets. | `list(string)` | `[]` | no | | hmac\_service\_accounts | List of HMAC service accounts to grant access to GCS. | `map(string)` | `{}` | no | +| ip\_filter | The IP filter configuration for a bucket. Map of lowercase unprefixed name => ip filter config object. See https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket#ip_filter-1 |
map(object({| `{}` | no | | labels | Labels to be attached to the buckets | `map(string)` | `{}` | no | | 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. |
mode = string
public_network_source = optional(object({
allowed_ip_cidr_ranges = list(string)
}))
vpc_network_sources = optional(list(object({
network = string
allowed_ip_cidr_ranges = list(string)
})))
allow_cross_org_vpcs = optional(bool)
allow_all_service_agent_access = optional(bool)
}))
set(object({| `[]` | no | | location | Bucket location. | `string` | `"EU"` | no | diff --git a/examples/multiple_buckets/main.tf b/examples/multiple_buckets/main.tf index 36ea879e..5cc1d276 100644 --- a/examples/multiple_buckets/main.tf +++ b/examples/multiple_buckets/main.tf @@ -75,4 +75,14 @@ module "cloud_storage" { default_event_based_hold = { "one" = true } + + ip_filter = { + "one" = { + mode = "Enabled" + public_network_source = { + allowed_ip_cidr_ranges = ["0.0.0.0/0", "::/0"] + } + allow_all_service_agent_access = true + } + } } diff --git a/main.tf b/main.tf index 345fd3be..022f3d35 100644 --- a/main.tf +++ b/main.tf @@ -172,6 +172,31 @@ resource "google_storage_bucket" "buckets" { retention_duration_seconds = lookup(soft_delete_policy.value, "retention_duration_seconds", null) } } + + dynamic "ip_filter" { + for_each = lookup(var.ip_filter, each.value, null) != null ? [lookup(var.ip_filter, each.value)] : [] + + content { + mode = ip_filter.value.mode + allow_cross_org_vpcs = ip_filter.value.allow_cross_org_vpcs + allow_all_service_agent_access = ip_filter.value.allow_all_service_agent_access + + dynamic "public_network_source" { + for_each = ip_filter.value.public_network_source != null ? [ip_filter.value.public_network_source] : [] + content { + allowed_ip_cidr_ranges = public_network_source.value.allowed_ip_cidr_ranges + } + } + + dynamic "vpc_network_sources" { + for_each = ip_filter.value.vpc_network_sources != null ? ip_filter.value.vpc_network_sources : [] + content { + network = vpc_network_sources.value.network + allowed_ip_cidr_ranges = vpc_network_sources.value.allowed_ip_cidr_ranges + } + } + } + } } resource "google_storage_bucket_iam_binding" "admins" { diff --git a/metadata.yaml b/metadata.yaml index 4055095b..cf0d4b56 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -288,6 +288,22 @@ spec: description: Soft delete policies to apply. Map of lowercase unprefixed name => soft delete policy. Format is the same as described in provider documentation https://www.terraform.io/docs/providers/google/r/storage_bucket.html#nested_soft_delete_policy varType: map(any) defaultValue: {} + - name: ip_filter + description: The IP filter configuration for a bucket. Map of lowercase unprefixed name => ip filter config object. See https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket#ip_filter-1 + varType: |- + map(object({ + mode = string + public_network_source = optional(object({ + allowed_ip_cidr_ranges = list(string) + })) + vpc_network_sources = optional(list(object({ + network = string + allowed_ip_cidr_ranges = list(string) + }))) + allow_cross_org_vpcs = optional(bool) + allow_all_service_agent_access = optional(bool) + })) + defaultValue: {} outputs: - name: apphub_service_uri description: URI in CAIS style to be used by Apphub. @@ -701,11 +717,11 @@ spec: roles: - level: Project roles: - - roles/resourcemanager.projectIamAdmin - - roles/serviceusage.serviceUsageAdmin - roles/storage.admin - roles/iam.serviceAccountAdmin - roles/iam.serviceAccountUser + - roles/resourcemanager.projectIamAdmin + - roles/serviceusage.serviceUsageAdmin services: - cloudresourcemanager.googleapis.com - iam.googleapis.com @@ -713,6 +729,6 @@ spec: - storage.googleapis.com providerVersions: - source: hashicorp/google - version: ">= 6.9.0, < 7" + version: ">= 6.37.0, < 8" - source: hashicorp/random version: ">= 2.1" diff --git a/modules/simple_bucket/README.md b/modules/simple_bucket/README.md index 307c2059..b8fbe069 100644 --- a/modules/simple_bucket/README.md +++ b/modules/simple_bucket/README.md @@ -47,6 +47,7 @@ Functional examples are included in the | hierarchical\_namespace | When set to true, hierarchical namespace is enable for this bucket. | `bool` | `false` | no | | iam\_members | The list of IAM members to grant permissions on the bucket. |
# 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 = object({
type = string
storage_class = optional(string)
})
# Object with keys:
# - age - (Optional) Minimum age of an object in days to satisfy this condition.
# - send_age_if_zero - (Optional) While set true, num_newer_versions value will be sent in the request even for zero value of the field.
# - 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.
# - matches_prefix - (Optional) One or more matching name prefixes to satisfy this condition.
# - matches_suffix - (Optional) One or more matching name suffixes to satisfy this condition.
# - num_newer_versions - (Optional) Relevant only for versioned objects. The number of newer versions of an object to satisfy this condition.
# - custom_time_before - (Optional) A date in the RFC 3339 format YYYY-MM-DD. This condition is satisfied when the customTime metadata for the object is set to an earlier date than the date used in this lifecycle condition.
# - days_since_custom_time - (Optional) The number of days from the Custom-Time metadata attribute after which this condition becomes true.
# - days_since_noncurrent_time - (Optional) Relevant only for versioned objects. Number of days elapsed since the noncurrent timestamp of an object.
# - noncurrent_time_before - (Optional) Relevant only for versioned objects. The date in RFC 3339 (e.g. 2017-06-13) when the object became nonconcurrent.
condition = object({
age = optional(number)
send_age_if_zero = optional(bool)
created_before = optional(string)
with_state = optional(string)
matches_storage_class = optional(string)
matches_prefix = optional(string)
matches_suffix = optional(string)
num_newer_versions = optional(number)
custom_time_before = optional(string)
days_since_custom_time = optional(number)
days_since_noncurrent_time = optional(number)
noncurrent_time_before = optional(string)
})
}))
list(object({| `[]` | no | | internal\_encryption\_config | Configuration for the creation of an internal Google Cloud Key Management Service (KMS) Key for use as Customer-managed encryption key (CMEK) for the GCS Bucket
role = string
member = string
}))
object({| `{}` | no | +| ip\_filter | The IP filter configuration for the bucket. Restricts access based on source IP addresses.
create_encryption_key = optional(bool, false)
prevent_destroy = optional(bool, false)
key_destroy_scheduled_duration = optional(string, null)
key_rotation_period = optional(string, "7776000s")
})
ip_filter = {Limits: Max 200 IP CIDR blocks, 25 VPC networks. May block some Google Cloud services.
mode = "Enabled"
public_network_source = {
allowed_ip_cidr_ranges = ["203.0.113.0/24"]
}
vpc_network_sources = [{
network = "projects/my-project/global/networks/my-vpc"
allowed_ip_cidr_ranges = ["10.0.0.0/8"]
}]
allow_cross_org_vpcs = true
allow_all_service_agent_access = true
}
object({| `null` | no | | labels | A set of key/value label pairs to assign to the bucket. | `map(string)` | `null` | no | | lifecycle\_rules | The bucket's Lifecycle Rules configuration. |
mode = string
public_network_source = optional(object({
allowed_ip_cidr_ranges = list(string)
}))
vpc_network_sources = optional(list(object({
network = string
allowed_ip_cidr_ranges = list(string)
})))
allow_cross_org_vpcs = optional(bool)
allow_all_service_agent_access = optional(bool)
})
list(object({| `[]` | no | | location | The location of the bucket. See https://cloud.google.com/storage/docs/locations. | `string` | n/a | yes | diff --git a/modules/simple_bucket/main.tf b/modules/simple_bucket/main.tf index 915ad20d..b401bbdf 100644 --- a/modules/simple_bucket/main.tf +++ b/modules/simple_bucket/main.tf @@ -119,6 +119,28 @@ resource "google_storage_bucket" "bucket" { retention_duration_seconds = lookup(soft_delete_policy.value, "retention_duration_seconds", null) } } + + dynamic "ip_filter" { + for_each = var.ip_filter == null ? [] : [var.ip_filter] + content { + mode = ip_filter.value.mode + allow_cross_org_vpcs = ip_filter.value.allow_cross_org_vpcs + allow_all_service_agent_access = ip_filter.value.allow_all_service_agent_access + dynamic "public_network_source" { + for_each = ip_filter.value.public_network_source != null ? [ip_filter.value.public_network_source] : [] + content { + allowed_ip_cidr_ranges = public_network_source.value.allowed_ip_cidr_ranges + } + } + dynamic "vpc_network_sources" { + for_each = ip_filter.value.vpc_network_sources != null ? ip_filter.value.vpc_network_sources : [] + content { + network = vpc_network_sources.value.network + allowed_ip_cidr_ranges = vpc_network_sources.value.allowed_ip_cidr_ranges + } + } + } + } } resource "google_storage_bucket_iam_member" "members" { diff --git a/modules/simple_bucket/metadata.yaml b/modules/simple_bucket/metadata.yaml index c02afcd8..1bafb277 100644 --- a/modules/simple_bucket/metadata.yaml +++ b/modules/simple_bucket/metadata.yaml @@ -205,6 +205,49 @@ spec: key_rotation_period = optional(string, "7776000s") }) defaultValue: {} + - name: ip_filter + description: | + The IP filter configuration for the bucket. Restricts access based on source IP addresses. + + - mode: "Enabled" or "Disabled" + - public_network_source: (Optional) Configure allowed public internet IP ranges + - vpc_network_sources: (Optional) Configure allowed VPC networks and IP ranges + - allow_cross_org_vpcs: (Optional) Allow VPC networks from different organizations + - allow_all_service_agent_access: (Optional) Allow Google Cloud service agents to access the bucket regardless of IP filtering + + Both public_network_source and vpc_network_sources can be configured together. + + Example: + ``` + ip_filter = { + mode = "Enabled" + public_network_source = { + allowed_ip_cidr_ranges = ["203.0.113.0/24"] + } + vpc_network_sources = [{ + network = "projects/my-project/global/networks/my-vpc" + allowed_ip_cidr_ranges = ["10.0.0.0/8"] + }] + allow_cross_org_vpcs = true + allow_all_service_agent_access = true + } + ``` + + Limits: Max 200 IP CIDR blocks, 25 VPC networks. May block some Google Cloud services. + See https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/storage_bucket#ip_filter-1 + varType: |- + object({ + mode = string + public_network_source = optional(object({ + allowed_ip_cidr_ranges = list(string) + })) + vpc_network_sources = optional(list(object({ + network = string + allowed_ip_cidr_ranges = list(string) + }))) + allow_cross_org_vpcs = optional(bool) + allow_all_service_agent_access = optional(bool) + }) outputs: - name: apphub_service_uri description: URI in CAIS style to be used by Apphub. @@ -361,4 +404,4 @@ spec: - storage.googleapis.com providerVersions: - source: hashicorp/google - version: ">= 6.9.0, < 7" + version: ">= 6.37.0, < 8" diff --git a/modules/simple_bucket/variables.tf b/modules/simple_bucket/variables.tf index 227ad69f..949d4dbd 100644 --- a/modules/simple_bucket/variables.tf +++ b/modules/simple_bucket/variables.tf @@ -211,3 +211,49 @@ variable "internal_encryption_config" { }) default = {} } + +variable "ip_filter" { + description = <
# 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 = object({
type = string
storage_class = optional(string)
})
# Object with keys:
# - age - (Optional) Minimum age of an object in days to satisfy this condition.
# - send_age_if_zero - (Optional) While set true, num_newer_versions value will be sent in the request even for zero value of the field.
# - 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) Storage Class of objects to satisfy this condition. Supported values include: MULTI_REGIONAL, REGIONAL, NEARLINE, COLDLINE, STANDARD, DURABLE_REDUCED_AVAILABILITY.
# - matches_prefix - (Optional) One or more matching name prefixes to satisfy this condition.
# - matches_suffix - (Optional) One or more matching name suffixes to satisfy this condition
# - num_newer_versions - (Optional) Relevant only for versioned objects. The number of newer versions of an object to satisfy this condition.
# - custom_time_before - (Optional) A date in the RFC 3339 format YYYY-MM-DD. This condition is satisfied when the customTime metadata for the object is set to an earlier date than the date used in this lifecycle condition.
# - days_since_custom_time - (Optional) Days since the date set in the customTime metadata for the object.
# - days_since_noncurrent_time - (Optional) Relevant only for versioned objects. Number of days elapsed since the noncurrent timestamp of an object.
# - noncurrent_time_before - (Optional) Relevant only for versioned objects. The date in RFC 3339 (e.g. 2017-06-13) when the object became nonconcurrent.
condition = object({
age = optional(number)
send_age_if_zero = optional(bool)
created_before = optional(string)
with_state = optional(string)
matches_storage_class = optional(string)
matches_prefix = optional(string)
matches_suffix = optional(string)
num_newer_versions = optional(number)
custom_time_before = optional(string)
days_since_custom_time = optional(number)
days_since_noncurrent_time = optional(number)
noncurrent_time_before = optional(string)
})
}))