diff --git a/modules/job-exec/metadata.yaml b/modules/job-exec/metadata.yaml index 4df1fe92..ebe4468c 100644 --- a/modules/job-exec/metadata.yaml +++ b/modules/job-exec/metadata.yaml @@ -317,13 +317,13 @@ spec: roles: - level: Project roles: - - roles/run.admin - - roles/iam.serviceAccountAdmin - roles/artifactregistry.admin - roles/iam.serviceAccountUser - roles/serviceusage.serviceUsageViewer - roles/cloudkms.admin - roles/resourcemanager.projectIamAdmin + - roles/run.admin + - roles/iam.serviceAccountAdmin services: - accesscontextmanager.googleapis.com - cloudbilling.googleapis.com diff --git a/modules/secure-cloud-run-core/README.md b/modules/secure-cloud-run-core/README.md index 7f0db181..58a468af 100644 --- a/modules/secure-cloud-run-core/README.md +++ b/modules/secure-cloud-run-core/README.md @@ -37,43 +37,53 @@ module "cloud_run_core" { | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| argument | Arguments passed to the ENTRYPOINT command, include these only if image entrypoint needs arguments. | `list(string)` | `[]` | no | +| argument | Arguments passed to the ENTRYPOINT command. | `list(string)` | `[]` | no | | certificate\_mode | The mode of the certificate (NONE or AUTOMATIC). | `string` | `"NONE"` | no | -| cloud\_armor\_policies\_name | Cloud Armor policy name already created in the project. If `create_cloud_armor_policies` is `false`, this variable must be provided, If `create_cloud_armor_policies` is `true`, this variable will be ignored. | `string` | `null` | no | +| cloud\_armor\_policies\_name | Existing Cloud Armor policy name if create\_cloud\_armor\_policies is false. | `string` | `null` | no | +| cloud\_run\_deletion\_protection | This field prevents Terraform from destroying or recreating the Cloud Run v2 Jobs and Services | `bool` | `false` | no | | cloud\_run\_sa | Service account to be used on Cloud Run. | `string` | n/a | yes | -| container\_command | Leave blank to use the ENTRYPOINT command defined in the container image, include these only if image entrypoint should be overwritten. | `list(string)` | `[]` | no | +| container\_command | Container entrypoint command. | `list(string)` | `[]` | no | | container\_concurrency | Concurrent request limits to the service. | `number` | `null` | no | -| create\_cloud\_armor\_policies | When `true`, the terraform will create the Cloud Armor policies. When `false`, the user must provide their own Cloud Armor name in `cloud_armor_policies_name`. | `bool` | `true` | no | +| create\_cloud\_armor\_policies | When true, create Cloud Armor policies. When false, provide existing name. | `bool` | `true` | no | | default\_rules | Default rule for Cloud Armor. |
map(object({
action = string
priority = string
versioned_expr = string
src_ip_ranges = list(string)
description = string
}))
|
{
"default_rule": {
"action": "allow",
"description": "Default allow all rule",
"priority": "2147483647",
"src_ip_ranges": [
"*"
],
"versioned_expr": "SRC_IPS_V1"
}
}
| no | | domain\_map\_annotations | Annotations to the domain map. | `map(string)` | `{}` | no | -| domain\_map\_labels | A set of key/value label pairs to assign to the Domain mapping. | `map(string)` | `{}` | no | -| encryption\_key | CMEK encryption key self-link expected in the format projects/PROJECT/locations/LOCATION/keyRings/KEY-RING/cryptoKeys/CRYPTO-KEY. | `string` | n/a | yes | +| domain\_map\_labels | Labels to assign to the Domain mapping. | `map(string)` | `{}` | no | +| enable\_prometheus\_sidecar | Enable Prometheus sidecar in Cloud Run instance. | `bool` | `false` | no | +| encryption\_key | CMEK encryption key self-link. | `string` | `null` | no | | env\_vars | Environment variables. |
list(object({
value = string
name = string
}))
| `[]` | no | +| execution\_environment | The execution environment (e.g., EXECUTION\_ENVIRONMENT\_GEN2, EXECUTION\_ENVIRONMENT\_GEN1). | `string` | `"EXECUTION_ENVIRONMENT_GEN2"` | no | | force\_override | Option to force override existing mapping. | `bool` | `false` | no | | generate\_revision\_name | Option to enable revision name generation. | `bool` | `true` | no | +| gpu\_zonal\_redundancy\_disabled | True if GPU zonal redundancy is disabled on this revision. | `bool` | `false` | no | +| iap\_members | Users/SAs to be given IAP access (if IAP is enabled). | `list(string)` | `[]` | no | | image | GAR hosted image URL to deploy. | `string` | n/a | yes | +| ingress | Ingress traffic sources allowed to call the service. | `string` | `"INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER"` | no | +| launch\_stage | The launch stage as defined by Google Cloud Platform Launch Stages. Cloud Run supports ALPHA, BETA, and GA. If no value is specified, GA is assumed. | `string` | `"GA"` | no | | lb\_name | Name for load balancer and associated resources. | `string` | `"tf-cr-lb"` | no | -| limits | Resource limits to the container. | `map(string)` | `null` | no | +| limits | Resource limits (memory, cpu, nvidia.com/gpu). | `map(string)` | `null` | no | +| liveness\_probe | Configuration for the liveness probe. |
object({
failure_threshold = optional(number)
initial_delay_seconds = optional(number)
timeout_seconds = optional(number)
period_seconds = optional(number)
http_get = optional(object({
path = optional(string)
port = optional(number)
http_headers = optional(list(object({
name = string
value = string
})))
}))
tcp_socket = optional(object({
port = number
}))
grpc = optional(object({
port = optional(number)
service = optional(string)
}))
})
| `null` | no | | location | The location where resources are going to be deployed. | `string` | n/a | yes | -| max\_scale\_instances | Sets the maximum number of container instances needed to handle all incoming requests or events from each revison from Cloud Run. For more information, access this [documentation](https://cloud.google.com/run/docs/about-instance-autoscaling). | `number` | `2` | no | -| members | Users/SAs to be given invoker access to the service with the prefix `serviceAccount:' for SAs and `user:` for users.` | `list(string)` | `[]` | no | -| min\_scale\_instances | Sets the minimum number of container instances needed to handle all incoming requests or events from each revison from Cloud Run. For more information, access this [documentation](https://cloud.google.com/run/docs/about-instance-autoscaling). | `number` | `1` | no | -| owasp\_rules | These are additional Cloud Armor rules for SQLi, XSS, LFI, RCE, RFI, Scannerdetection, Protocolattack and Sessionfixation (requires Cloud Armor default\_rule). |
map(object({
action = string
priority = string
expression = string
}))
|
{
"rule_canary": {
"action": "deny(403)",
"expression": "evaluatePreconfiguredExpr('rce-v33-stable')",
"priority": "1003"
},
"rule_lfi": {
"action": "deny(403)",
"expression": "evaluatePreconfiguredExpr('lfi-v33-stable')",
"priority": "1002"
},
"rule_protocolattack": {
"action": "deny(403)",
"expression": "evaluatePreconfiguredExpr('protocolattack-v33-stable')",
"priority": "1006"
},
"rule_rfi": {
"action": "deny(403)",
"expression": "evaluatePreconfiguredExpr('rfi-v33-stable')",
"priority": "1004"
},
"rule_scannerdetection": {
"action": "deny(403)",
"expression": "evaluatePreconfiguredExpr('scannerdetection-v33-stable')",
"priority": "1005"
},
"rule_sessionfixation": {
"action": "deny(403)",
"expression": "evaluatePreconfiguredExpr('sessionfixation-v33-stable')",
"priority": "1007"
},
"rule_sqli": {
"action": "deny(403)",
"expression": "evaluatePreconfiguredExpr('sqli-v33-stable')",
"priority": "1000"
},
"rule_xss": {
"action": "deny(403)",
"expression": "evaluatePreconfiguredExpr('xss-v33-stable')",
"priority": "1001"
}
}
| no | -| ports | Port which the container listens to (http1 or h2c). |
object({
name = string
port = number
})
|
{
"name": "http1",
"port": 8080
}
| no | +| max\_scale\_instances | Maximum number of container instances. | `number` | `100` | no | +| members | Users/SAs to be given invoker access. | `list(string)` | `[]` | no | +| min\_scale\_instances | Minimum number of container instances. | `number` | `0` | no | +| node\_selector | Node Selector describes the hardware requirements of the GPU resource. [More info](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloud_run_v2_service#nested_template_node_selector). |
object({
accelerator = string
})
| `null` | no | +| owasp\_rules | Additional Cloud Armor rules (SQLi, XSS, etc). |
map(object({
action = string
priority = string
expression = string
}))
|
{
"rule_canary": {
"action": "deny(403)",
"expression": "evaluatePreconfiguredExpr('rce-v33-stable')",
"priority": "1003"
},
"rule_lfi": {
"action": "deny(403)",
"expression": "evaluatePreconfiguredExpr('lfi-v33-stable')",
"priority": "1002"
},
"rule_protocolattack": {
"action": "deny(403)",
"expression": "evaluatePreconfiguredExpr('protocolattack-v33-stable')",
"priority": "1006"
},
"rule_rfi": {
"action": "deny(403)",
"expression": "evaluatePreconfiguredExpr('rfi-v33-stable')",
"priority": "1004"
},
"rule_scannerdetection": {
"action": "deny(403)",
"expression": "evaluatePreconfiguredExpr('scannerdetection-v33-stable')",
"priority": "1005"
},
"rule_sessionfixation": {
"action": "deny(403)",
"expression": "evaluatePreconfiguredExpr('sessionfixation-v33-stable')",
"priority": "1007"
},
"rule_sqli": {
"action": "deny(403)",
"expression": "evaluatePreconfiguredExpr('sqli-v33-stable')",
"priority": "1000"
},
"rule_xss": {
"action": "deny(403)",
"expression": "evaluatePreconfiguredExpr('xss-v33-stable')",
"priority": "1001"
}
}
| no | +| ports | Port which the container listens to. |
object({
name = string
port = number
})
|
{
"name": "http1",
"port": 8080
}
| no | | project\_id | The project where cloud run is going to be deployed. | `string` | n/a | yes | -| region | Location for load balancer and Cloud Run resources. | `string` | n/a | yes | -| requests | Resource requests to the container. | `map(string)` | `{}` | no | -| service\_labels | A set of key/value label pairs to assign to the service. | `map(string)` | `{}` | no | +| region | Location for load balancer and Cloud Run resources (usually same as location). | `string` | n/a | yes | +| service\_labels | Labels to assign to the service. | `map(string)` | `{}` | no | | service\_name | The name of the Cloud Run service to create. | `string` | n/a | yes | -| ssl\_certificates | A object with a list of domains to auto-generate SSL certificates or a list of SSL Certificates self-links in the pattern `projects//global/sslCertificates/` to be used by Load Balancer. |
object({
ssl_certificates_self_links = list(string)
generate_certificates_for_domains = list(string)
})
| n/a | yes | -| template\_labels | A set of key/value label pairs to assign to the container metadata. | `map(string)` | `{}` | no | -| timeout\_seconds | Timeout for each request. | `number` | `120` | no | -| traffic\_split | Managing traffic routing to the service. |
list(object({
latest_revision = bool
percent = number
revision_name = string
tag = string
}))
|
[
{
"latest_revision": true,
"percent": 100,
"revision_name": "v1-0-0",
"tag": null
}
]
| no | -| verified\_domain\_name | List of custom Domain Name. | `list(string)` | n/a | yes | -| volume\_mounts | [Beta] Volume Mounts to be attached to the container (when using secret). |
list(object({
mount_path = string
name = string
}))
| `[]` | no | +| ssl\_certificates | A object with a list of domains to auto-generate SSL certificates or a list of SSL Certificates self-links. |
object({
ssl_certificates_self_links = list(string)
generate_certificates_for_domains = list(string)
})
| n/a | yes | +| startup\_probe | Configuration for the startup probe. |
object({
failure_threshold = optional(number)
initial_delay_seconds = optional(number)
timeout_seconds = optional(number)
period_seconds = optional(number)
http_get = optional(object({
path = optional(string)
port = optional(number)
http_headers = optional(list(object({
name = string
value = string
})))
}))
tcp_socket = optional(object({
port = number
}))
grpc = optional(object({
port = optional(number)
service = optional(string)
}))
})
| `null` | no | +| template\_labels | Labels to assign to the container metadata. | `map(string)` | `{}` | no | +| timeout\_seconds | Timeout for each request in seconds. | `number` | `120` | no | +| traffic\_split | Managing traffic routing to the service. |
list(object({
latest_revision = optional(bool)
percent = number
revision_name = optional(string)
tag = optional(string)
}))
|
[
{
"latest_revision": true,
"percent": 100
}
]
| no | +| verified\_domain\_name | List of custom Domain Name. | `list(string)` | `[]` | no | +| volume\_mounts | Volume Mounts to be attached to the container. |
list(object({
mount_path = string
name = string
}))
| `[]` | no | | volumes | [Beta] Volumes needed for environment variables (when using secret). |
list(object({
name = string
secret = set(object({
secret_name = string
items = map(string)
}))
}))
| `[]` | no | -| vpc\_connector\_id | VPC Connector id in the format projects/PROJECT/locations/LOCATION/connectors/NAME. | `string` | n/a | yes | -| vpc\_egress\_value | Sets VPC Egress firewall rule. Supported values are all-traffic, all (deprecated), and private-ranges-only. all-traffic and all provide the same functionality. all is deprecated but will continue to be supported. Prefer all-traffic. | `string` | `"private-ranges-only"` | no | +| vpc\_connector\_id | VPC Connector id. If provided, Direct VPC Egress settings are ignored. | `string` | `null` | no | +| vpc\_egress\_value | Sets VPC Egress firewall rule (e.g. PRIVATE\_RANGES\_ONLY, ALL\_TRAFFIC). | `string` | `"PRIVATE_RANGES_ONLY"` | no | +| vpc\_network\_interface | List of network interfaces for Direct VPC Egress (Cloud Run v2). |
object({
network = optional(string)
subnetwork = optional(string)
tags = optional(list(string))
})
| `null` | no | ## Outputs diff --git a/modules/secure-cloud-run-core/main.tf b/modules/secure-cloud-run-core/main.tf index 84127be9..e2d67eba 100644 --- a/modules/secure-cloud-run-core/main.tf +++ b/modules/secure-cloud-run-core/main.tf @@ -1,5 +1,5 @@ /** - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,74 +15,156 @@ */ locals { - annotations_for_template = { - "autoscaling.knative.dev/maxScale" = var.max_scale_instances, - "autoscaling.knative.dev/minScale" = var.min_scale_instances, - "run.googleapis.com/vpc-access-connector" = var.vpc_connector_id, - "run.googleapis.com/vpc-access-egress" = var.vpc_egress_value - } + env_vars_map = { for item in var.env_vars : item.name => item.value } + + main_container = { + container_name = var.service_name + container_image = var.image + container_command = var.container_command + container_args = var.argument + working_dir = null + + ports = { + name = var.ports.name + container_port = var.ports.port + } + + resources = { + limits = var.limits + cpu_idle = true + startup_cpu_boost = false + } + + env_vars = local.env_vars_map + env_secret_vars = {} + + startup_probe = var.startup_probe + liveness_probe = var.liveness_probe - conditional_annotations = { - secret = length(local.secrets_alias) == 0 ? {} : { "run.googleapis.com/secrets" = join(", ", toset(local.secrets_alias)) } + depends_on_container = [] + volume_mounts = var.volume_mounts } - secrets = distinct(flatten([ - for secret in var.volumes : [ - for secret_name in secret.secret : [ - { - "name" : secret.name, - "secret_name" : secret_name.secret_name, - "path" : secret_name.items.path + traffic_config = [ + for t in var.traffic_split : { + percent = t.percent + type = null + revision = lookup(t, "latest_revision", true) ? null : lookup(t, "revision_name", null) + tag = lookup(t, "tag", null) + } + ] + + volumes_config = [ + for v in var.volumes : { + name = v.name + + secret = length(v.secret) > 0 ? { + secret = v.secret[0].secret + default_mode = try(tostring(v.secret[0].default_mode), null) + items = length(try(v.secret[0].items, [])) > 0 ? { + path = v.secret[0].items[0].path + version = v.secret[0].items[0].version + mode = try(tostring(v.secret[0].items[0].mode), null) + } : null + } : null + + cloud_sql_instance = length(v.cloud_sql_instance) > 0 ? { + instances = v.cloud_sql_instance[0].instances + } : null + + gcs = length(v.gcs) > 0 ? { + bucket = v.gcs[0].bucket + read_only = try(tostring(v.gcs[0].read_only), null) + } : null + + nfs = length(v.nfs) > 0 ? { + server = v.nfs[0].server + path = v.nfs[0].path + read_only = try(tostring(v.nfs[0].read_only), null) + } : null + + empty_dir = length(v.empty_dir) > 0 ? { + medium = v.empty_dir[0].medium + size_limit = v.empty_dir[0].size_limit + } : null + } + ] + + secrets_list = distinct(flatten([ + for v in var.volumes : [ + for s in v.secret : [ + for item in(s.items != null ? s.items : []) : { + name = v.name + secret_name = s.secret + path = item.path } ] ] ])) - secrets_alias = [ - for secret in local.secrets : - "${secret.name}:${secret.path}${secret.secret_name}" - ] + vpc_config = var.vpc_connector_id != null ? { + connector = var.vpc_connector_id + egress = var.vpc_egress_value + network_interfaces = null + } : (var.vpc_network_interface != null ? { + connector = null + egress = var.vpc_egress_value + network_interfaces = var.vpc_network_interface + } : null) } module "cloud_run" { - source = "../.." - - service_name = var.service_name - project_id = var.project_id - location = var.location - image = var.image - service_account_email = var.cloud_run_sa - encryption_key = var.encryption_key - members = var.members - env_vars = var.env_vars - generate_revision_name = var.generate_revision_name - traffic_split = var.traffic_split - service_labels = var.service_labels - template_labels = var.template_labels - container_concurrency = var.container_concurrency - timeout_seconds = var.timeout_seconds - volumes = var.volumes - limits = var.limits - requests = var.requests - ports = var.ports - argument = var.argument - container_command = var.container_command - volume_mounts = var.volume_mounts + source = "../v2" + + project_id = var.project_id + service_name = var.service_name + location = var.location + description = "Managed by Terraform" + + service_account = var.cloud_run_sa + create_service_account = false + + containers = [local.main_container] + + revision = var.generate_revision_name ? null : "${var.service_name}-rev" + + template_scaling = { + min_instance_count = var.min_scale_instances + max_instance_count = var.max_scale_instances + } + + vpc_access = local.vpc_config + + ingress = var.ingress + execution_environment = var.execution_environment + + volumes = local.volumes_config + encryption_key = var.encryption_key + + service_labels = var.service_labels + template_labels = var.template_labels + service_annotations = {} + template_annotations = {} + + timeout = "${var.timeout_seconds}s" + max_instance_request_concurrency = var.container_concurrency != null ? tostring(var.container_concurrency) : null + + traffic = local.traffic_config + + members = var.members + iap_members = var.iap_members + launch_stage = var.launch_stage + node_selector = var.node_selector + gpu_zonal_redundancy_disabled = var.gpu_zonal_redundancy_disabled + enable_prometheus_sidecar = var.enable_prometheus_sidecar + cloud_run_deletion_protection = var.cloud_run_deletion_protection + force_override = var.force_override certificate_mode = var.certificate_mode domain_map_labels = var.domain_map_labels domain_map_annotations = var.domain_map_annotations verified_domain_name = var.verified_domain_name - service_annotations = { - "run.googleapis.com/ingress" = "internal-and-cloud-load-balancing" - } - - template_annotations = merge( - local.annotations_for_template, - local.conditional_annotations["secret"] - ) - depends_on = [ time_sleep.wait_30_seconds ] @@ -103,8 +185,34 @@ resource "time_sleep" "wait_30_seconds" { } resource "google_secret_manager_secret_iam_member" "member" { - for_each = { for secret in local.secrets : secret.name => secret } - secret_id = "${each.value.path}${each.value.secret_name}" + for_each = { for s in local.secrets_list : "${s.name}-${s.path}" => s } + secret_id = each.value.secret_name role = "roles/secretmanager.secretAccessor" member = "serviceAccount:${var.cloud_run_sa}" } + +resource "google_cloud_run_domain_mapping" "domain_map" { + for_each = toset(var.verified_domain_name) + provider = google-beta + location = var.location + name = each.value + project = var.project_id + + metadata { + labels = var.domain_map_labels + annotations = var.domain_map_annotations + namespace = var.project_id + } + + spec { + route_name = module.cloud_run.service_name + force_override = var.force_override + certificate_mode = var.certificate_mode + } + + lifecycle { + ignore_changes = [ + metadata[0].annotations["run.googleapis.com/operation-id"], + ] + } +} diff --git a/modules/secure-cloud-run-core/metadata.yaml b/modules/secure-cloud-run-core/metadata.yaml index c66d7052..96434daa 100644 --- a/modules/secure-cloud-run-core/metadata.yaml +++ b/modules/secure-cloud-run-core/metadata.yaml @@ -56,12 +56,16 @@ spec: location: examples/v2_with_iap interfaces: variables: + - name: project_id + description: The project where cloud run is going to be deployed. + varType: string + required: true - name: location description: The location where resources are going to be deployed. varType: string required: true - - name: project_id - description: The project where cloud run is going to be deployed. + - name: region + description: Location for load balancer and Cloud Run resources (usually same as location). varType: string required: true - name: service_name @@ -76,18 +80,127 @@ spec: description: Service account to be used on Cloud Run. varType: string required: true + - name: execution_environment + description: The execution environment (e.g., EXECUTION_ENVIRONMENT_GEN2, EXECUTION_ENVIRONMENT_GEN1). + varType: string + defaultValue: EXECUTION_ENVIRONMENT_GEN2 + - name: env_vars + description: Environment variables. + varType: |- + list(object({ + value = string + name = string + })) + defaultValue: [] + - name: ports + description: Port which the container listens to. + varType: |- + object({ + name = string + port = number + }) + defaultValue: + name: http1 + port: 8080 + - name: argument + description: Arguments passed to the ENTRYPOINT command. + varType: list(string) + defaultValue: [] + - name: container_command + description: Container entrypoint command. + varType: list(string) + defaultValue: [] + - name: limits + description: Resource limits (memory, cpu, nvidia.com/gpu). + varType: map(string) + - name: container_concurrency + description: Concurrent request limits to the service. + varType: number + - name: timeout_seconds + description: Timeout for each request in seconds. + varType: number + defaultValue: 120 + - name: startup_probe + description: Configuration for the startup probe. + varType: |- + object({ + failure_threshold = optional(number) + initial_delay_seconds = optional(number) + timeout_seconds = optional(number) + period_seconds = optional(number) + http_get = optional(object({ + path = optional(string) + port = optional(number) + http_headers = optional(list(object({ + name = string + value = string + }))) + })) + tcp_socket = optional(object({ + port = number + })) + grpc = optional(object({ + port = optional(number) + service = optional(string) + })) + }) + - name: liveness_probe + description: Configuration for the liveness probe. + varType: |- + object({ + failure_threshold = optional(number) + initial_delay_seconds = optional(number) + timeout_seconds = optional(number) + period_seconds = optional(number) + http_get = optional(object({ + path = optional(string) + port = optional(number) + http_headers = optional(list(object({ + name = string + value = string + }))) + })) + tcp_socket = optional(object({ + port = number + })) + grpc = optional(object({ + port = optional(number) + service = optional(string) + })) + }) + - name: ingress + description: Ingress traffic sources allowed to call the service. + varType: string + defaultValue: INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER - name: vpc_connector_id - description: VPC Connector id in the format projects/PROJECT/locations/LOCATION/connectors/NAME. + description: VPC Connector id. If provided, Direct VPC Egress settings are ignored. varType: string - required: true + - name: vpc_egress_value + description: Sets VPC Egress firewall rule (e.g. PRIVATE_RANGES_ONLY, ALL_TRAFFIC). + varType: string + defaultValue: PRIVATE_RANGES_ONLY + - name: vpc_network_interface + description: List of network interfaces for Direct VPC Egress (Cloud Run v2). + varType: |- + object({ + network = optional(string) + subnetwork = optional(string) + tags = optional(list(string)) + }) - name: encryption_key - description: CMEK encryption key self-link expected in the format projects/PROJECT/locations/LOCATION/keyRings/KEY-RING/cryptoKeys/CRYPTO-KEY. + description: CMEK encryption key self-link. varType: string - required: true - - name: region - description: Location for load balancer and Cloud Run resources. + - name: lb_name + description: Name for load balancer and associated resources. + varType: string + defaultValue: tf-cr-lb + - name: create_cloud_armor_policies + description: When true, create Cloud Armor policies. When false, provide existing name. + varType: bool + defaultValue: true + - name: cloud_armor_policies_name + description: Existing Cloud Armor policy name if create_cloud_armor_policies is false. varType: string - required: true - name: default_rules description: Default rule for Cloud Armor. varType: |- @@ -107,7 +220,7 @@ spec: - "*" versioned_expr: SRC_IPS_V1 - name: owasp_rules - description: These are additional Cloud Armor rules for SQLi, XSS, LFI, RCE, RFI, Scannerdetection, Protocolattack and Sessionfixation (requires Cloud Armor default_rule). + description: Additional Cloud Armor rules (SQLi, XSS, etc). varType: |- map(object({ action = string @@ -147,103 +260,77 @@ spec: action: deny(403) expression: evaluatePreconfiguredExpr('xss-v33-stable') priority: "1001" - - name: lb_name - description: Name for load balancer and associated resources. - varType: string - defaultValue: tf-cr-lb - - name: env_vars - description: Environment variables. + - name: ssl_certificates + description: A object with a list of domains to auto-generate SSL certificates or a list of SSL Certificates self-links. + varType: |- + object({ + ssl_certificates_self_links = list(string) + generate_certificates_for_domains = list(string) + }) + required: true + - name: volume_mounts + description: Volume Mounts to be attached to the container. varType: |- list(object({ - value = string - name = string + mount_path = string + name = string })) defaultValue: [] - - name: members - description: Users/SAs to be given invoker access to the service with the prefix `serviceAccount:' for SAs and `user:` for users. - varType: list(string) + - name: volumes + description: "[Beta] Volumes needed for environment variables (when using secret)." + varType: |- + list(object({ + name = string + secret = set(object({ + secret_name = string + items = map(string) + })) + })) defaultValue: [] - name: generate_revision_name description: Option to enable revision name generation. varType: bool defaultValue: true + - name: min_scale_instances + description: Minimum number of container instances. + varType: number + defaultValue: 0 + - name: max_scale_instances + description: Maximum number of container instances. + varType: number + defaultValue: 100 - name: traffic_split description: Managing traffic routing to the service. varType: |- list(object({ - latest_revision = bool + latest_revision = optional(bool) percent = number - revision_name = string - tag = string + revision_name = optional(string) + tag = optional(string) })) defaultValue: - latest_revision: true percent: 100 - revision_name: v1-0-0 - tag: null + - name: members + description: Users/SAs to be given invoker access. + varType: list(string) + defaultValue: [] + - name: iap_members + description: Users/SAs to be given IAP access (if IAP is enabled). + varType: list(string) + defaultValue: [] - name: service_labels - description: A set of key/value label pairs to assign to the service. + description: Labels to assign to the service. varType: map(string) defaultValue: {} - name: template_labels - description: A set of key/value label pairs to assign to the container metadata. + description: Labels to assign to the container metadata. varType: map(string) defaultValue: {} - - name: container_concurrency - description: Concurrent request limits to the service. - varType: number - - name: timeout_seconds - description: Timeout for each request. - varType: number - defaultValue: 120 - - name: volumes - description: "[Beta] Volumes needed for environment variables (when using secret)." - varType: |- - list(object({ - name = string - secret = set(object({ - secret_name = string - items = map(string) - })) - })) - defaultValue: [] - - name: limits - description: Resource limits to the container. - varType: map(string) - - name: requests - description: Resource requests to the container. - varType: map(string) - defaultValue: {} - - name: ports - description: Port which the container listens to (http1 or h2c). - varType: |- - object({ - name = string - port = number - }) - defaultValue: - name: http1 - port: 8080 - - name: argument - description: Arguments passed to the ENTRYPOINT command, include these only if image entrypoint needs arguments. - varType: list(string) - defaultValue: [] - - name: container_command - description: Leave blank to use the ENTRYPOINT command defined in the container image, include these only if image entrypoint should be overwritten. - varType: list(string) - defaultValue: [] - - name: volume_mounts - description: "[Beta] Volume Mounts to be attached to the container (when using secret)." - varType: |- - list(object({ - mount_path = string - name = string - })) - defaultValue: [] - name: verified_domain_name description: List of custom Domain Name. varType: list(string) - required: true + defaultValue: [] - name: force_override description: Option to force override existing mapping. varType: bool @@ -253,40 +340,35 @@ spec: varType: string defaultValue: NONE - name: domain_map_labels - description: A set of key/value label pairs to assign to the Domain mapping. + description: Labels to assign to the Domain mapping. varType: map(string) defaultValue: {} - name: domain_map_annotations description: Annotations to the domain map. varType: map(string) defaultValue: {} - - name: create_cloud_armor_policies - description: When `true`, the terraform will create the Cloud Armor policies. When `false`, the user must provide their own Cloud Armor name in `cloud_armor_policies_name`. + - name: cloud_run_deletion_protection + description: This field prevents Terraform from destroying or recreating the Cloud Run v2 Jobs and Services varType: bool - defaultValue: true - - name: cloud_armor_policies_name - description: Cloud Armor policy name already created in the project. If `create_cloud_armor_policies` is `false`, this variable must be provided, If `create_cloud_armor_policies` is `true`, this variable will be ignored. - varType: string - - name: max_scale_instances - description: Sets the maximum number of container instances needed to handle all incoming requests or events from each revison from Cloud Run. For more information, access this [documentation](https://cloud.google.com/run/docs/about-instance-autoscaling). - varType: number - defaultValue: 2 - - name: min_scale_instances - description: Sets the minimum number of container instances needed to handle all incoming requests or events from each revison from Cloud Run. For more information, access this [documentation](https://cloud.google.com/run/docs/about-instance-autoscaling). - varType: number - defaultValue: 1 - - name: vpc_egress_value - description: Sets VPC Egress firewall rule. Supported values are all-traffic, all (deprecated), and private-ranges-only. all-traffic and all provide the same functionality. all is deprecated but will continue to be supported. Prefer all-traffic. - varType: string - defaultValue: private-ranges-only - - name: ssl_certificates - description: A object with a list of domains to auto-generate SSL certificates or a list of SSL Certificates self-links in the pattern `projects//global/sslCertificates/` to be used by Load Balancer. + defaultValue: false + - name: enable_prometheus_sidecar + description: Enable Prometheus sidecar in Cloud Run instance. + varType: bool + defaultValue: false + - name: gpu_zonal_redundancy_disabled + description: True if GPU zonal redundancy is disabled on this revision. + varType: bool + defaultValue: false + - name: node_selector + description: Node Selector describes the hardware requirements of the GPU resource. [More info](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloud_run_v2_service#nested_template_node_selector). varType: |- object({ - ssl_certificates_self_links = list(string) - generate_certificates_for_domains = list(string) + accelerator = string }) - required: true + - name: launch_stage + description: The launch stage as defined by Google Cloud Platform Launch Stages. Cloud Run supports ALPHA, BETA, and GA. If no value is specified, GA is assumed. + varType: string + defaultValue: GA outputs: - name: domain_map_id description: Unique Identifier for the created domain map. diff --git a/modules/secure-cloud-run-core/variables.tf b/modules/secure-cloud-run-core/variables.tf index d80fe4e5..381e6078 100644 --- a/modules/secure-cloud-run-core/variables.tf +++ b/modules/secure-cloud-run-core/variables.tf @@ -1,5 +1,5 @@ /** - * Copyright 2022 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,15 +14,18 @@ * limitations under the License. */ - +variable "project_id" { + description = "The project where cloud run is going to be deployed." + type = string +} variable "location" { description = "The location where resources are going to be deployed." type = string } -variable "project_id" { - description = "The project where cloud run is going to be deployed." +variable "region" { + description = "Location for load balancer and Cloud Run resources (usually same as location)." type = string } @@ -41,19 +44,165 @@ variable "cloud_run_sa" { type = string } +variable "execution_environment" { + description = "The execution environment (e.g., EXECUTION_ENVIRONMENT_GEN2, EXECUTION_ENVIRONMENT_GEN1)." + type = string + default = "EXECUTION_ENVIRONMENT_GEN2" +} + +variable "env_vars" { + type = list(object({ + value = string + name = string + })) + description = "Environment variables." + default = [] +} + +variable "ports" { + description = "Port which the container listens to." + type = object({ + name = string + port = number + }) + default = { + name = "http1" + port = 8080 + } +} + +variable "argument" { + description = "Arguments passed to the ENTRYPOINT command." + type = list(string) + default = [] +} + +variable "container_command" { + description = "Container entrypoint command." + type = list(string) + default = [] +} + +variable "limits" { + description = "Resource limits (memory, cpu, nvidia.com/gpu)." + type = map(string) + default = null +} + +variable "container_concurrency" { + description = "Concurrent request limits to the service." + type = number + default = null +} + +variable "timeout_seconds" { + description = "Timeout for each request in seconds." + type = number + default = 120 +} + +variable "startup_probe" { + description = "Configuration for the startup probe." + type = object({ + failure_threshold = optional(number) + initial_delay_seconds = optional(number) + timeout_seconds = optional(number) + period_seconds = optional(number) + http_get = optional(object({ + path = optional(string) + port = optional(number) + http_headers = optional(list(object({ + name = string + value = string + }))) + })) + tcp_socket = optional(object({ + port = number + })) + grpc = optional(object({ + port = optional(number) + service = optional(string) + })) + }) + default = null +} + +variable "liveness_probe" { + description = "Configuration for the liveness probe." + type = object({ + failure_threshold = optional(number) + initial_delay_seconds = optional(number) + timeout_seconds = optional(number) + period_seconds = optional(number) + http_get = optional(object({ + path = optional(string) + port = optional(number) + http_headers = optional(list(object({ + name = string + value = string + }))) + })) + tcp_socket = optional(object({ + port = number + })) + grpc = optional(object({ + port = optional(number) + service = optional(string) + })) + }) + default = null +} + +variable "ingress" { + description = "Ingress traffic sources allowed to call the service." + type = string + default = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER" +} + variable "vpc_connector_id" { - description = "VPC Connector id in the format projects/PROJECT/locations/LOCATION/connectors/NAME." + description = "VPC Connector id. If provided, Direct VPC Egress settings are ignored." + type = string + default = null +} + +variable "vpc_egress_value" { + description = "Sets VPC Egress firewall rule (e.g. PRIVATE_RANGES_ONLY, ALL_TRAFFIC)." type = string + default = "PRIVATE_RANGES_ONLY" +} + +variable "vpc_network_interface" { + description = "List of network interfaces for Direct VPC Egress (Cloud Run v2)." + type = object({ + network = optional(string) + subnetwork = optional(string) + tags = optional(list(string)) + }) + default = null } variable "encryption_key" { - description = "CMEK encryption key self-link expected in the format projects/PROJECT/locations/LOCATION/keyRings/KEY-RING/cryptoKeys/CRYPTO-KEY." + description = "CMEK encryption key self-link." type = string + default = null } -variable "region" { - description = "Location for load balancer and Cloud Run resources." +variable "lb_name" { + description = "Name for load balancer and associated resources." type = string + default = "tf-cr-lb" +} + +variable "create_cloud_armor_policies" { + type = bool + description = "When true, create Cloud Armor policies. When false, provide existing name." + default = true +} + +variable "cloud_armor_policies_name" { + type = string + description = "Existing Cloud Armor policy name if create_cloud_armor_policies is false." + default = null } variable "default_rules" { @@ -77,7 +226,7 @@ variable "default_rules" { } variable "owasp_rules" { - description = "These are additional Cloud Armor rules for SQLi, XSS, LFI, RCE, RFI, Scannerdetection, Protocolattack and Sessionfixation (requires Cloud Armor default_rule)." + description = "Additional Cloud Armor rules (SQLi, XSS, etc)." default = { rule_sqli = { action = "deny(403)" @@ -127,25 +276,38 @@ variable "owasp_rules" { })) } -variable "lb_name" { - description = "Name for load balancer and associated resources." - type = string - default = "tf-cr-lb" +variable "ssl_certificates" { + type = object({ + ssl_certificates_self_links = list(string) + generate_certificates_for_domains = list(string) + }) + validation { + condition = (!(length(var.ssl_certificates.ssl_certificates_self_links) == 0 && length(var.ssl_certificates.generate_certificates_for_domains) == 0) || + !(length(var.ssl_certificates.ssl_certificates_self_links) > 0 && length(var.ssl_certificates.generate_certificates_for_domains) > 0)) + error_message = "You must provide a SSL Certificate self-link or at least one domain to a SSL Certificate be generated." + } + description = "A object with a list of domains to auto-generate SSL certificates or a list of SSL Certificates self-links." } -variable "env_vars" { +variable "volume_mounts" { type = list(object({ - value = string - name = string + mount_path = string + name = string })) - description = "Environment variables." + description = "Volume Mounts to be attached to the container." default = [] } -variable "members" { - type = list(string) - description = "Users/SAs to be given invoker access to the service with the prefix `serviceAccount:' for SAs and `user:` for users." - default = [] +variable "volumes" { + description = "[Beta] Volumes needed for environment variables (when using secret)." + type = list(object({ + name = string + secret = set(object({ + secret_name = string + items = map(string) + })) + })) + default = [] } variable "generate_revision_name" { @@ -154,112 +316,60 @@ variable "generate_revision_name" { default = true } +variable "min_scale_instances" { + description = "Minimum number of container instances." + type = number + default = 0 +} + +variable "max_scale_instances" { + description = "Maximum number of container instances." + type = number + default = 100 +} + variable "traffic_split" { description = "Managing traffic routing to the service." type = list(object({ - latest_revision = bool + latest_revision = optional(bool) percent = number - revision_name = string - tag = string + revision_name = optional(string) + tag = optional(string) })) default = [{ latest_revision = true percent = 100 - revision_name = "v1-0-0" - tag = null }] } -variable "service_labels" { - description = "A set of key/value label pairs to assign to the service." - type = map(string) - default = {} -} - -// Metadata -variable "template_labels" { - description = "A set of key/value label pairs to assign to the container metadata." - type = map(string) - default = {} -} - -// template spec -variable "container_concurrency" { - description = "Concurrent request limits to the service." - type = number - default = null -} - -variable "timeout_seconds" { - description = "Timeout for each request." - type = number - default = 120 -} - -variable "volumes" { - description = "[Beta] Volumes needed for environment variables (when using secret)." - type = list(object({ - name = string - secret = set(object({ - secret_name = string - items = map(string) - })) - })) - default = [] -} - -# template spec container -# resources -# cpu = (core count * 1000)m -# memory = (size) in Mi/Gi -variable "limits" { - description = "Resource limits to the container." - type = map(string) - default = null -} -variable "requests" { - description = "Resource requests to the container." - type = map(string) - default = {} -} - -variable "ports" { - description = "Port which the container listens to (http1 or h2c)." - type = object({ - name = string - port = number - }) - default = { - name = "http1" - port = 8080 - } -} - -variable "argument" { - description = "Arguments passed to the ENTRYPOINT command, include these only if image entrypoint needs arguments." +variable "members" { type = list(string) + description = "Users/SAs to be given invoker access." default = [] } -variable "container_command" { - description = "Leave blank to use the ENTRYPOINT command defined in the container image, include these only if image entrypoint should be overwritten." +variable "iap_members" { type = list(string) + description = "Users/SAs to be given IAP access (if IAP is enabled)." default = [] } -variable "volume_mounts" { - type = list(object({ - mount_path = string - name = string - })) - description = "[Beta] Volume Mounts to be attached to the container (when using secret)." - default = [] +variable "service_labels" { + description = "Labels to assign to the service." + type = map(string) + default = {} +} + +variable "template_labels" { + description = "Labels to assign to the container metadata." + type = map(string) + default = {} } -// Domain Mapping variable "verified_domain_name" { description = "List of custom Domain Name." type = list(string) + default = [] } variable "force_override" { @@ -275,7 +385,7 @@ variable "certificate_mode" { } variable "domain_map_labels" { - description = "A set of key/value label pairs to assign to the Domain mapping." + description = "Labels to assign to the Domain mapping." type = map(string) default = {} } @@ -286,45 +396,39 @@ variable "domain_map_annotations" { default = {} } -variable "create_cloud_armor_policies" { +variable "cloud_run_deletion_protection" { type = bool - description = "When `true`, the terraform will create the Cloud Armor policies. When `false`, the user must provide their own Cloud Armor name in `cloud_armor_policies_name`." - default = true + description = "This field prevents Terraform from destroying or recreating the Cloud Run v2 Jobs and Services" + default = false } -variable "cloud_armor_policies_name" { - type = string - description = "Cloud Armor policy name already created in the project. If `create_cloud_armor_policies` is `false`, this variable must be provided, If `create_cloud_armor_policies` is `true`, this variable will be ignored." - default = null +variable "enable_prometheus_sidecar" { + type = bool + description = "Enable Prometheus sidecar in Cloud Run instance." + default = false } -variable "max_scale_instances" { - description = "Sets the maximum number of container instances needed to handle all incoming requests or events from each revison from Cloud Run. For more information, access this [documentation](https://cloud.google.com/run/docs/about-instance-autoscaling)." - type = number - default = 2 +variable "gpu_zonal_redundancy_disabled" { + type = bool + description = "True if GPU zonal redundancy is disabled on this revision." + default = false } -variable "min_scale_instances" { - description = "Sets the minimum number of container instances needed to handle all incoming requests or events from each revison from Cloud Run. For more information, access this [documentation](https://cloud.google.com/run/docs/about-instance-autoscaling)." - type = number - default = 1 +variable "node_selector" { + type = object({ + accelerator = string + }) + description = "Node Selector describes the hardware requirements of the GPU resource. [More info](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloud_run_v2_service#nested_template_node_selector)." + default = null } -variable "vpc_egress_value" { - description = "Sets VPC Egress firewall rule. Supported values are all-traffic, all (deprecated), and private-ranges-only. all-traffic and all provide the same functionality. all is deprecated but will continue to be supported. Prefer all-traffic." +variable "launch_stage" { type = string - default = "private-ranges-only" -} + description = "The launch stage as defined by Google Cloud Platform Launch Stages. Cloud Run supports ALPHA, BETA, and GA. If no value is specified, GA is assumed." + default = "GA" -variable "ssl_certificates" { - type = object({ - ssl_certificates_self_links = list(string) - generate_certificates_for_domains = list(string) - }) validation { - condition = (!(length(var.ssl_certificates.ssl_certificates_self_links) == 0 && length(var.ssl_certificates.generate_certificates_for_domains) == 0) || - !(length(var.ssl_certificates.ssl_certificates_self_links) > 0 && length(var.ssl_certificates.generate_certificates_for_domains) > 0)) - error_message = "You must provide a SSL Certificate self-link or at least one domain to a SSL Certificate be generated." + condition = contains(["UNIMPLEMENTED", "PRELAUNCH", "EARLY_ACCESS", "ALPHA", "BETA", "GA", "DEPRECATED"], var.launch_stage) + error_message = "Allowed values for launch_stage are \"UNIMPLEMENTED\", \"PRELAUNCH\", or \"EARLY_ACCESS\", or \"DEPRECATED\", or \"ALPHA\", or \"BETA\", or \"GA\"." } - description = "A object with a list of domains to auto-generate SSL certificates or a list of SSL Certificates self-links in the pattern `projects//global/sslCertificates/` to be used by Load Balancer." } diff --git a/modules/secure-cloud-run-security/metadata.yaml b/modules/secure-cloud-run-security/metadata.yaml index be40d0ec..e4130ed2 100644 --- a/modules/secure-cloud-run-security/metadata.yaml +++ b/modules/secure-cloud-run-security/metadata.yaml @@ -133,13 +133,13 @@ spec: roles: - level: Project roles: + - roles/iam.serviceAccountUser + - roles/serviceusage.serviceUsageViewer - roles/cloudkms.admin - roles/resourcemanager.projectIamAdmin - roles/run.admin - roles/iam.serviceAccountAdmin - roles/artifactregistry.admin - - roles/iam.serviceAccountUser - - roles/serviceusage.serviceUsageViewer services: - accesscontextmanager.googleapis.com - cloudbilling.googleapis.com diff --git a/modules/secure-cloud-run/README.md b/modules/secure-cloud-run/README.md index 5158b49b..65d66c21 100644 --- a/modules/secure-cloud-run/README.md +++ b/modules/secure-cloud-run/README.md @@ -78,18 +78,23 @@ module "secure_cloud_run" { | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| +| argument | Arguments passed to the ENTRYPOINT command, include these only if image entrypoint needs arguments. | `list(string)` | `[]` | no | | artifact\_registry\_repository\_location | Artifact Registry Repository location to grant serverless identity viewer role. | `string` | `null` | no | | artifact\_registry\_repository\_name | Artifact Registry Repository name to grant serverless identity viewer role | `string` | `null` | no | | artifact\_registry\_repository\_project\_id | Artifact Registry Repository Project ID to grant serverless identity viewer role. | `string` | `null` | no | | cloud\_armor\_policies\_name | Cloud Armor policy name already created in the project. If `create_cloud_armor_policies` is `false`, this variable must be provided, If `create_cloud_armor_policies` is `true`, this variable will be ignored. | `string` | `null` | no | +| cloud\_run\_deletion\_protection | This field prevents Terraform from destroying or recreating the Cloud Run v2 Jobs and Services | `bool` | `false` | no | | cloud\_run\_sa | Service account to be used on Cloud Run. | `string` | n/a | yes | | connector\_name | The name for the connector to be created. | `string` | `"serverless-vpc-connector"` | no | | create\_cloud\_armor\_policies | When `true`, the terraform will create the Cloud Armor policies. When `false`, the user must provide their own Cloud Armor name in `cloud_armor_policies_name`. | `bool` | `true` | no | | create\_subnet | The subnet will be created with the subnet\_name variable if true. When false, it will use the subnet\_name for the subnet. | `bool` | `true` | no | +| enable\_prometheus\_sidecar | Enable Prometheus sidecar in Cloud Run instance. | `bool` | `false` | no | | env\_vars | Environment variables (cleartext) |
list(object({
value = string
name = string
}))
| `[]` | no | | folder\_id | The folder ID to apply the policy to. | `string` | `""` | no | +| gpu\_zonal\_redundancy\_disabled | True if GPU zonal redundancy is disabled on this revision. | `bool` | `false` | no | | grant\_artifact\_register\_reader | When true it will grant permission to read an image from your artifact registry. When true, you must provide `artifact_registry_repository_project_id`, `artifact_registry_repository_location` and `artifact_registry_repository_name`. | `bool` | `false` | no | | groups | Groups which will have roles assigned.
The Serverless Administrators email group which the following roles will be added: Cloud Run Admin, Compute Network Viewer and Compute Network User.
The Serverless Security Administrators email group which the following roles will be added: Cloud Run Viewer, Cloud KMS Viewer and Artifact Registry Reader.
The Cloud Run Developer email group which the following roles will be added: Cloud Run Developer, Artifact Registry Writer and Cloud KMS CryptoKey Encrypter.
The Cloud Run User email group which the following roles will be added: Cloud Run Invoker. |
object({
group_serverless_administrator = optional(string, null)
group_serverless_security_administrator = optional(string, null)
group_cloud_run_developer = optional(string, null)
group_cloud_run_user = optional(string, null)
})
| `{}` | no | +| iap\_members | Valid only when launch stage is set to 'BETA'. IAP is enabled automatically when users or service accounts (SAs) are provided. Use allUsers for public access, allAuthenticatedUsers for any Google-authenticated user, or specify individual users/SAs. [More info](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/iap_web_cloud_run_service_iam#member/members-2) | `list(string)` | `[]` | no | | image | Image url to be deployed on Cloud Run. | `string` | n/a | yes | | ip\_cidr\_range | The range of internal addresses that are owned by the subnetwork and which is going to be used by VPC Connector. For example, 10.0.0.0/28 or 192.168.0.0/28. Ranges must be unique and non-overlapping within a network. Only IPv4 is supported. | `string` | n/a | yes | | key\_name | The name of KMS Key to be created and used in Cloud Run. | `string` | `"cloud-run-kms-key"` | no | @@ -97,12 +102,15 @@ module "secure_cloud_run" { | key\_rotation\_period | Period of key rotation in seconds. | `string` | `"2592000s"` | no | | keyring\_name | Keyring name. | `string` | `"cloud-run-kms-keyring"` | no | | kms\_project\_id | The project where KMS will be created. | `string` | n/a | yes | +| launch\_stage | The launch stage as defined by Google Cloud Platform Launch Stages. Cloud Run supports ALPHA, BETA, and GA. If no value is specified, GA is assumed. | `string` | `"GA"` | no | | location | The location where resources are going to be deployed. | `string` | n/a | yes | | max\_scale\_instances | Sets the maximum number of container instances needed to handle all incoming requests or events from each revison from Cloud Run. For more information, access this [documentation](https://cloud.google.com/run/docs/about-instance-autoscaling). | `number` | `2` | no | | members | Users/SAs to be given invoker access to the service with the prefix `serviceAccount:' for SAs and `user:` for users.` | `list(string)` | `[]` | no | | min\_scale\_instances | Sets the minimum number of container instances needed to handle all incoming requests or events from each revison from Cloud Run. For more information, access this [documentation](https://cloud.google.com/run/docs/about-instance-autoscaling). | `number` | `1` | no | +| node\_selector | Node Selector describes the hardware requirements of the GPU resource. [More info](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloud_run_v2_service#nested_template_node_selector). |
object({
accelerator = string
})
| `null` | no | | organization\_id | The organization ID to apply the policy to. | `string` | `""` | no | | policy\_for | Policy Root: set one of the following values to determine where the policy is applied. Possible values: ["project", "folder", "organization"]. | `string` | `"project"` | no | +| ports | Port which the container listens to. |
object({
name = string
port = number
})
|
{
"name": "http1",
"port": 8080
}
| no | | prevent\_destroy | Set the `prevent_destroy` lifecycle attribute on the Cloud KMS key. | `bool` | `true` | no | | region | Location for load balancer and Cloud Run resources. | `string` | n/a | yes | | resource\_names\_suffix | A suffix to concat in the end of the network resources names being created. | `string` | `null` | no | @@ -113,7 +121,8 @@ module "secure_cloud_run" { | subnet\_name | Subnet name to be re-used to create Serverless Connector. | `string` | `null` | no | | verified\_domain\_name | List of Custom Domain Name | `list(string)` | `[]` | no | | volumes | [Beta] Volumes needed for environment variables (when using secret). |
list(object({
name = string
secret = set(object({
secret_name = string
items = map(string)
}))
}))
| `[]` | no | -| vpc\_egress\_value | Sets VPC Egress firewall rule. Supported values are all-traffic, all (deprecated), and private-ranges-only. all-traffic and all provide the same functionality. all is deprecated but will continue to be supported. Prefer all-traffic. | `string` | `"private-ranges-only"` | no | +| vpc\_egress\_value | Sets VPC Egress firewall rule. Supported values are ALL\_TRAFFIC, ALL (deprecated), and PRIVATE\_RANGES\_ONLY. all-traffic and all provide the same functionality. all is deprecated but will continue to be supported. Prefer all-traffic. | `string` | `"PRIVATE_RANGES_ONLY"` | no | +| vpc\_network\_interface | List of network interfaces for Direct VPC Egress (Cloud Run v2). |
object({
network = optional(string)
subnetwork = optional(string)
tags = optional(list(string))
})
| `null` | no | | vpc\_project\_id | The host project for the shared vpc. | `string` | n/a | yes | ## Outputs diff --git a/modules/secure-cloud-run/main.tf b/modules/secure-cloud-run/main.tf index 9d3a75e3..ad90756e 100644 --- a/modules/secure-cloud-run/main.tf +++ b/modules/secure-cloud-run/main.tf @@ -127,24 +127,34 @@ resource "google_project_iam_member" "serverless_service_agent" { module "cloud_run_core" { source = "../secure-cloud-run-core" - service_name = var.service_name - location = var.location - project_id = var.serverless_project_id - image = var.image - cloud_run_sa = var.cloud_run_sa - vpc_connector_id = module.cloud_run_network.connector_id - encryption_key = module.cloud_run_security.key_self_link - env_vars = var.env_vars - members = var.members - region = var.region - verified_domain_name = var.verified_domain_name - create_cloud_armor_policies = var.create_cloud_armor_policies - cloud_armor_policies_name = var.cloud_armor_policies_name - vpc_egress_value = var.vpc_egress_value - min_scale_instances = var.min_scale_instances - max_scale_instances = var.max_scale_instances - volumes = var.volumes - ssl_certificates = var.ssl_certificates + service_name = var.service_name + location = var.location + project_id = var.serverless_project_id + image = var.image + cloud_run_sa = var.cloud_run_sa + vpc_connector_id = module.cloud_run_network.connector_id + encryption_key = module.cloud_run_security.key_self_link + env_vars = var.env_vars + members = var.members + region = var.region + verified_domain_name = var.verified_domain_name + create_cloud_armor_policies = var.create_cloud_armor_policies + cloud_armor_policies_name = var.cloud_armor_policies_name + vpc_egress_value = var.vpc_egress_value + min_scale_instances = var.min_scale_instances + max_scale_instances = var.max_scale_instances + volumes = var.volumes + ssl_certificates = var.ssl_certificates + vpc_network_interface = var.vpc_network_interface + iap_members = var.iap_members + launch_stage = var.launch_stage + node_selector = var.node_selector + gpu_zonal_redundancy_disabled = var.gpu_zonal_redundancy_disabled + enable_prometheus_sidecar = var.enable_prometheus_sidecar + cloud_run_deletion_protection = var.cloud_run_deletion_protection + argument = var.argument + ports = var.ports + depends_on = [ module.serverless_project_apis, diff --git a/modules/secure-cloud-run/metadata.yaml b/modules/secure-cloud-run/metadata.yaml index f648489e..22ff11e8 100644 --- a/modules/secure-cloud-run/metadata.yaml +++ b/modules/secure-cloud-run/metadata.yaml @@ -176,9 +176,9 @@ spec: varType: number defaultValue: 1 - name: vpc_egress_value - description: Sets VPC Egress firewall rule. Supported values are all-traffic, all (deprecated), and private-ranges-only. all-traffic and all provide the same functionality. all is deprecated but will continue to be supported. Prefer all-traffic. + description: Sets VPC Egress firewall rule. Supported values are ALL_TRAFFIC, ALL (deprecated), and PRIVATE_RANGES_ONLY. all-traffic and all provide the same functionality. all is deprecated but will continue to be supported. Prefer all-traffic. varType: string - defaultValue: private-ranges-only + defaultValue: PRIVATE_RANGES_ONLY - name: create_cloud_armor_policies description: When `true`, the terraform will create the Cloud Armor policies. When `false`, the user must provide their own Cloud Armor name in `cloud_armor_policies_name`. varType: bool @@ -219,6 +219,54 @@ spec: generate_certificates_for_domains = list(string) }) required: true + - name: vpc_network_interface + description: List of network interfaces for Direct VPC Egress (Cloud Run v2). + varType: |- + object({ + network = optional(string) + subnetwork = optional(string) + tags = optional(list(string)) + }) + - name: cloud_run_deletion_protection + description: This field prevents Terraform from destroying or recreating the Cloud Run v2 Jobs and Services + varType: bool + defaultValue: false + - name: enable_prometheus_sidecar + description: Enable Prometheus sidecar in Cloud Run instance. + varType: bool + defaultValue: false + - name: gpu_zonal_redundancy_disabled + description: True if GPU zonal redundancy is disabled on this revision. + varType: bool + defaultValue: false + - name: node_selector + description: Node Selector describes the hardware requirements of the GPU resource. [More info](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloud_run_v2_service#nested_template_node_selector). + varType: |- + object({ + accelerator = string + }) + - name: launch_stage + description: The launch stage as defined by Google Cloud Platform Launch Stages. Cloud Run supports ALPHA, BETA, and GA. If no value is specified, GA is assumed. + varType: string + defaultValue: GA + - name: iap_members + description: Valid only when launch stage is set to 'BETA'. IAP is enabled automatically when users or service accounts (SAs) are provided. Use allUsers for public access, allAuthenticatedUsers for any Google-authenticated user, or specify individual users/SAs. [More info](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/iap_web_cloud_run_service_iam#member\/members-2) + varType: list(string) + defaultValue: [] + - name: argument + description: Arguments passed to the ENTRYPOINT command, include these only if image entrypoint needs arguments. + varType: list(string) + defaultValue: [] + - name: ports + description: Port which the container listens to. + varType: |- + object({ + name = string + port = number + }) + defaultValue: + name: http1 + port: 8080 outputs: - name: cloud_services_sa description: Service Account for Cloud Run Service. @@ -250,13 +298,13 @@ spec: roles: - level: Project roles: + - roles/run.admin - roles/iam.serviceAccountAdmin - roles/artifactregistry.admin - roles/iam.serviceAccountUser - roles/serviceusage.serviceUsageViewer - roles/cloudkms.admin - roles/resourcemanager.projectIamAdmin - - roles/run.admin services: - accesscontextmanager.googleapis.com - cloudbilling.googleapis.com diff --git a/modules/secure-cloud-run/variables.tf b/modules/secure-cloud-run/variables.tf index 406fe31e..8ad5151c 100644 --- a/modules/secure-cloud-run/variables.tf +++ b/modules/secure-cloud-run/variables.tf @@ -188,9 +188,9 @@ variable "min_scale_instances" { } variable "vpc_egress_value" { - description = "Sets VPC Egress firewall rule. Supported values are all-traffic, all (deprecated), and private-ranges-only. all-traffic and all provide the same functionality. all is deprecated but will continue to be supported. Prefer all-traffic." + description = "Sets VPC Egress firewall rule. Supported values are ALL_TRAFFIC, ALL (deprecated), and PRIVATE_RANGES_ONLY. all-traffic and all provide the same functionality. all is deprecated but will continue to be supported. Prefer all-traffic." type = string - default = "private-ranges-only" + default = "PRIVATE_RANGES_ONLY" } variable "create_cloud_armor_policies" { @@ -254,3 +254,75 @@ variable "ssl_certificates" { } description = "A object with a list of domains to auto-generate SSL certificates or a list of SSL Certificates self-links in the pattern `projects//global/sslCertificates/` to be used by Load Balancer." } + +variable "vpc_network_interface" { + description = "List of network interfaces for Direct VPC Egress (Cloud Run v2)." + type = object({ + network = optional(string) + subnetwork = optional(string) + tags = optional(list(string)) + }) + default = null +} + +variable "cloud_run_deletion_protection" { + type = bool + description = "This field prevents Terraform from destroying or recreating the Cloud Run v2 Jobs and Services" + default = false +} + +variable "enable_prometheus_sidecar" { + type = bool + description = "Enable Prometheus sidecar in Cloud Run instance." + default = false +} + +variable "gpu_zonal_redundancy_disabled" { + type = bool + description = "True if GPU zonal redundancy is disabled on this revision." + default = false +} + +variable "node_selector" { + type = object({ + accelerator = string + }) + description = "Node Selector describes the hardware requirements of the GPU resource. [More info](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloud_run_v2_service#nested_template_node_selector)." + default = null +} + +variable "launch_stage" { + type = string + description = "The launch stage as defined by Google Cloud Platform Launch Stages. Cloud Run supports ALPHA, BETA, and GA. If no value is specified, GA is assumed." + default = "GA" + + validation { + condition = contains(["UNIMPLEMENTED", "PRELAUNCH", "EARLY_ACCESS", "ALPHA", "BETA", "GA", "DEPRECATED"], var.launch_stage) + error_message = "Allowed values for launch_stage are \"UNIMPLEMENTED\", \"PRELAUNCH\", or \"EARLY_ACCESS\", or \"DEPRECATED\", or \"ALPHA\", or \"BETA\", or \"GA\"." + } +} + +variable "iap_members" { + type = list(string) + description = "Valid only when launch stage is set to 'BETA'. IAP is enabled automatically when users or service accounts (SAs) are provided. Use allUsers for public access, allAuthenticatedUsers for any Google-authenticated user, or specify individual users/SAs. [More info](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/iap_web_cloud_run_service_iam#member\\/members-2)" + default = [] +} + +variable "argument" { + description = "Arguments passed to the ENTRYPOINT command, include these only if image entrypoint needs arguments." + type = list(string) + default = [] +} + +variable "ports" { + description = "Port which the container listens to." + type = object({ + name = string + port = number + }) + default = { + name = "http1" + port = 8080 + } +} + diff --git a/modules/secure-serverless-net/metadata.yaml b/modules/secure-serverless-net/metadata.yaml index 1eda0bdd..829dba90 100644 --- a/modules/secure-serverless-net/metadata.yaml +++ b/modules/secure-serverless-net/metadata.yaml @@ -120,13 +120,13 @@ spec: roles: - level: Project roles: + - roles/artifactregistry.admin - roles/iam.serviceAccountUser - roles/serviceusage.serviceUsageViewer - roles/cloudkms.admin - roles/resourcemanager.projectIamAdmin - roles/run.admin - roles/iam.serviceAccountAdmin - - roles/artifactregistry.admin services: - accesscontextmanager.googleapis.com - cloudbilling.googleapis.com diff --git a/modules/secure-serverless-net/network.tf b/modules/secure-serverless-net/network.tf index 21792bfe..3a00e95b 100644 --- a/modules/secure-serverless-net/network.tf +++ b/modules/secure-serverless-net/network.tf @@ -17,6 +17,7 @@ locals { subnet_name = var.create_subnet ? "${var.subnet_name}${local.suffix}" : var.subnet_name } + resource "google_compute_subnetwork" "vpc_subnetwork" { count = var.create_subnet ? 1 : 0 @@ -35,6 +36,8 @@ resource "google_compute_subnetwork" "vpc_subnetwork" { } resource "google_vpc_access_connector" "serverless_connector" { + count = var.connector_name != null ? 1 : 0 + name = "${var.connector_name}${local.suffix}" region = var.location project = var.connector_on_host_project ? var.vpc_project_id : var.serverless_project_id diff --git a/modules/secure-serverless-net/outputs.tf b/modules/secure-serverless-net/outputs.tf index d3d7ed6f..764c42a8 100644 --- a/modules/secure-serverless-net/outputs.tf +++ b/modules/secure-serverless-net/outputs.tf @@ -15,7 +15,7 @@ */ output "connector_id" { - value = google_vpc_access_connector.serverless_connector.id + value = var.connector_name != null ? google_vpc_access_connector.serverless_connector[0].id : null description = "VPC serverless connector ID." } diff --git a/modules/v2/README.md b/modules/v2/README.md index 619282fa..40833a13 100644 --- a/modules/v2/README.md +++ b/modules/v2/README.md @@ -51,15 +51,19 @@ Functional examples are included in the | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | binary\_authorization | Settings for the Binary Authorization feature. |
object({
breakglass_justification = optional(bool) # If present, indicates to use Breakglass using this justification. If useDefault is False, then it must be empty. For more information on breakglass, [see](https://cloud.google.com/binary-authorization/docs/using-breakglass)
use_default = optional(bool) #If True, indicates to use the default project's binary authorization policy. If False, binary authorization will be disabled.
})
| `null` | no | +| certificate\_mode | The mode of the certificate (NONE or AUTOMATIC). | `string` | `"NONE"` | no | | client | Arbitrary identifier for the API client and version identifier |
object({
name = optional(string, null)
version = optional(string, null)
})
| `{}` | no | | cloud\_run\_deletion\_protection | This field prevents Terraform from destroying or recreating the Cloud Run jobs and services | `bool` | `true` | no | | containers | Container images for the service |
list(object({
container_name = optional(string, null)
container_image = string
working_dir = optional(string, null)
depends_on_container = optional(list(string), null)
container_args = optional(list(string), null)
container_command = optional(list(string), null)
env_vars = optional(map(string), {})
env_secret_vars = optional(map(object({
secret = string
version = string
})), {})
volume_mounts = optional(list(object({
name = string
mount_path = string
})), [])
ports = optional(object({
name = optional(string, "http1")
container_port = optional(number, 8080)
}), {})
resources = optional(object({
limits = optional(object({
cpu = optional(string)
memory = optional(string)
nvidia_gpu = optional(string)
}))
cpu_idle = optional(bool, true)
startup_cpu_boost = optional(bool, false)
}), {})
startup_probe = optional(object({
failure_threshold = optional(number, null)
initial_delay_seconds = optional(number, null)
timeout_seconds = optional(number, null)
period_seconds = optional(number, null)
http_get = optional(object({
path = optional(string)
port = optional(string)
http_headers = optional(list(object({
name = string
value = string
})), [])
}), null)
tcp_socket = optional(object({
port = optional(number)
}), null)
grpc = optional(object({
port = optional(number)
service = optional(string)
}), null)
}), null)
liveness_probe = optional(object({
failure_threshold = optional(number, null)
initial_delay_seconds = optional(number, null)
timeout_seconds = optional(number, null)
period_seconds = optional(number, null)
http_get = optional(object({
path = optional(string)
port = optional(string)
http_headers = optional(list(object({
name = string
value = string
})), [])
}), null)
tcp_socket = optional(object({
port = optional(number)
}), null)
grpc = optional(object({
port = optional(number)
service = optional(string)
}), null)
}), null)
}))
| n/a | yes | | create\_service\_account | Create a new service account for cloud run service | `bool` | `true` | no | | custom\_audiences | One or more custom audiences that you want this service to support. Specify each custom audience as the full URL in a string. [Refer](https://cloud.google.com/run/docs/configuring/custom-audiences) | `list(string)` | `null` | no | | description | Cloud Run service description. This field currently has a 512-character limit. | `string` | `null` | no | +| domain\_map\_annotations | Annotations to the domain map. | `map(string)` | `{}` | no | +| domain\_map\_labels | A set of key/value label pairs to assign to the Domain mapping. | `map(string)` | `{}` | no | | enable\_prometheus\_sidecar | Enable Prometheus sidecar in Cloud Run instance. | `bool` | `false` | no | | encryption\_key | A reference to a customer managed encryption key (CMEK) to use to encrypt this container image. This is optional. | `string` | `null` | no | | execution\_environment | The sandbox environment to host this Revision. | `string` | `"EXECUTION_ENVIRONMENT_GEN2"` | no | +| force\_override | Option to force override existing mapping. | `bool` | `false` | no | | gpu\_zonal\_redundancy\_disabled | True if GPU zonal redundancy is disabled on this revision. | `bool` | `false` | no | | iap\_members | Valid only when launch stage is set to 'BETA'. IAP is enabled automatically when users or service accounts (SAs) are provided. Use allUsers for public access, allAuthenticatedUsers for any Google-authenticated user, or specify individual users/SAs. [More info](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/iap_web_cloud_run_service_iam#member/members-2) | `list(string)` | `[]` | no | | ingress | Restricts network access to your Cloud Run service | `string` | `"INGRESS_TRAFFIC_ALL"` | no | @@ -82,6 +86,7 @@ Functional examples are included in the | template\_scaling | Maximum and minimum number of instances for this Revision |
object({
min_instance_count = optional(number)
max_instance_count = optional(number)
})
| `null` | no | | timeout | Max allowed time for an instance to respond to a request. A duration in seconds with up to nine fractional digits, ending with 's' | `string` | `null` | no | | traffic | Specifies how to distribute traffic over a collection of Revisions belonging to the Service. If traffic is empty or not provided, defaults to 100% traffic to the latest Ready Revision. |
list(object({
type = optional(string, "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST")
percent = optional(number, 100)
revision = optional(string, null)
tag = optional(string, null)
}))
| `[]` | no | +| verified\_domain\_name | List of custom Domain Name. | `list(string)` | `[]` | no | | volumes | Volumes needed for environment variables (when using secret) |
list(object({
name = string
secret = optional(object({
secret = string
default_mode = optional(string)
items = optional(object({
path = string
version = optional(string)
mode = optional(string)
}))
}))
cloud_sql_instance = optional(object({
instances = optional(list(string))
}))
empty_dir = optional(object({
medium = optional(string)
size_limit = optional(string)
}))
gcs = optional(object({
bucket = string
read_only = optional(string)
}))
nfs = optional(object({
server = string
path = string
read_only = optional(string)
}))
}))
| `[]` | no | | vpc\_access | Configure this to enable your service to send traffic to a Virtual Private Cloud. Set egress to ALL\_TRAFFIC or PRIVATE\_RANGES\_ONLY. Choose a connector or network\_interfaces (for direct VPC egress). [More info](https://cloud.google.com/run/docs/configuring/connecting-vpc) |
object({
connector = optional(string)
egress = optional(string)
network_interfaces = optional(object({
network = optional(string)
subnetwork = optional(string)
tags = optional(list(string))
}))
})
| `null` | no | @@ -91,6 +96,8 @@ Functional examples are included in the |------|-------------| | apphub\_service\_uri | Service URI in CAIS style to be used by Apphub. | | creator | Email address of the authenticated creator. | +| domain\_map\_id | Unique Identifier for the created domain map | +| domain\_map\_status | Status of Domain mapping | | effective\_annotations | All of annotations (key/value pairs) present on the resource in GCP, including the annotations configured through Terraform, other clients and services. | | last\_modifier | Email address of the last authenticated modifier. | | latest\_created\_revision | Name of the last created revision. See comments in reconciling for additional information on reconciliation process in Cloud Run. | @@ -98,10 +105,13 @@ Functional examples are included in the | location | Location in which the Cloud Run service was created | | observed\_generation | The generation of this Service currently serving traffic. | | project\_id | Google Cloud project in which the service was created | +| revision | Deployed revision for the service (Full Resource Name) | | service\_account\_id | Service account id and email | -| service\_id | Unique Identifier for the created service with format projects/{{project}}/locations/{{location}}/services/{{name}} | +| service\_id | Unique Identifier for the created service | | service\_name | Name of the created service | +| service\_status | Status of the created service | | service\_uri | The main URI in which this Service is serving traffic. | +| service\_url | The URL on which the deployed service is available | | traffic\_statuses | Detailed status information for corresponding traffic targets. | diff --git a/modules/v2/main.tf b/modules/v2/main.tf index 83a27d5e..4aea3c31 100644 --- a/modules/v2/main.tf +++ b/modules/v2/main.tf @@ -15,7 +15,7 @@ */ data "google_compute_default_service_account" "default" { - count = local.create_service_account == false && var.service_account == null ? 1 : 0 + count = var.create_service_account ? 0 : 1 project = var.project_id } @@ -24,9 +24,9 @@ locals { var.service_account != null ? var.service_account : ( - var.create_service_account + local.create_service_account ? google_service_account.sa[0].email - : null + : data.google_compute_default_service_account.default[0].email ) ) create_service_account = var.create_service_account ? var.service_account == null : false @@ -407,3 +407,29 @@ resource "google_cloud_run_v2_service_iam_member" "authorize_iap_p4sa" { role = "roles/run.invoker" member = google_project_service_identity.iap_p4sa[count.index].member } + +resource "google_cloud_run_domain_mapping" "domain_map" { + for_each = toset(var.verified_domain_name) + provider = google-beta + location = google_cloud_run_v2_service.main.location + name = each.value + project = google_cloud_run_v2_service.main.project + + metadata { + labels = var.domain_map_labels + annotations = var.domain_map_annotations + namespace = var.project_id + } + + spec { + route_name = google_cloud_run_v2_service.main.name + force_override = var.force_override + certificate_mode = var.certificate_mode + } + + lifecycle { + ignore_changes = [ + metadata[0].annotations["run.googleapis.com/operation-id"], + ] + } +} diff --git a/modules/v2/metadata.yaml b/modules/v2/metadata.yaml index 408f64d7..74c3c515 100644 --- a/modules/v2/metadata.yaml +++ b/modules/v2/metadata.yaml @@ -640,6 +640,26 @@ spec: description: The sandbox environment to host this Revision. varType: string defaultValue: EXECUTION_ENVIRONMENT_GEN2 + - name: verified_domain_name + description: List of custom Domain Name. + varType: list(string) + defaultValue: [] + - name: certificate_mode + description: The mode of the certificate (NONE or AUTOMATIC). + varType: string + defaultValue: NONE + - name: force_override + description: Option to force override existing mapping. + varType: bool + defaultValue: false + - name: domain_map_labels + description: A set of key/value label pairs to assign to the Domain mapping. + varType: map(string) + defaultValue: {} + - name: domain_map_annotations + description: Annotations to the domain map. + varType: map(string) + defaultValue: {} outputs: - name: apphub_service_uri description: Service URI in CAIS style to be used by Apphub. @@ -651,6 +671,10 @@ spec: - name: creator description: Email address of the authenticated creator. type: string + - name: domain_map_id + description: Unique Identifier for the created domain map + - name: domain_map_status + description: Status of Domain mapping - name: effective_annotations description: All of annotations (key/value pairs) present on the resource in GCP, including the annotations configured through Terraform, other clients and services. type: @@ -674,6 +698,8 @@ spec: - name: project_id description: Google Cloud project in which the service was created type: string + - name: revision + description: Deployed revision for the service (Full Resource Name) - name: service_account_id description: Service account id and email type: @@ -682,14 +708,18 @@ spec: id: string member: string - name: service_id - description: Unique Identifier for the created service with format projects/{{project}}/locations/{{location}}/services/{{name}} + description: Unique Identifier for the created service type: string - name: service_name description: Name of the created service type: string + - name: service_status + description: Status of the created service - name: service_uri description: The main URI in which this Service is serving traffic. type: string + - name: service_url + description: The URL on which the deployed service is available - name: traffic_statuses description: Detailed status information for corresponding traffic targets. type: @@ -704,14 +734,14 @@ spec: roles: - level: Project roles: + - roles/iam.serviceAccountUser + - roles/serviceusage.serviceUsageViewer + - roles/resourcemanager.projectIamAdmin - roles/compute.viewer - roles/iap.admin - roles/artifactregistry.reader - roles/run.admin - roles/iam.serviceAccountAdmin - - roles/iam.serviceAccountUser - - roles/serviceusage.serviceUsageViewer - - roles/resourcemanager.projectIamAdmin services: - cloudresourcemanager.googleapis.com - compute.googleapis.com diff --git a/modules/v2/outputs.tf b/modules/v2/outputs.tf index 4e76d00f..797b53bd 100644 --- a/modules/v2/outputs.tf +++ b/modules/v2/outputs.tf @@ -14,31 +14,11 @@ * limitations under the License. */ -output "project_id" { - value = google_cloud_run_v2_service.main.project - description = "Google Cloud project in which the service was created" -} - output "service_uri" { value = google_cloud_run_v2_service.main.uri description = "The main URI in which this Service is serving traffic." } -output "service_id" { - value = google_cloud_run_v2_service.main.id - description = "Unique Identifier for the created service with format projects/{{project}}/locations/{{location}}/services/{{name}}" -} - -output "service_name" { - value = google_cloud_run_v2_service.main.name - description = "Name of the created service" -} - -output "location" { - value = google_cloud_run_v2_service.main.location - description = "Location in which the Cloud Run service was created" -} - output "creator" { value = google_cloud_run_v2_service.main.creator description = "Email address of the authenticated creator." @@ -79,11 +59,55 @@ output "service_account_id" { value = local.service_account_output } +output "service_name" { + value = google_cloud_run_v2_service.main.name + description = "Name of the created service" +} + +output "revision" { + value = google_cloud_run_v2_service.main.latest_ready_revision + description = "Deployed revision for the service (Full Resource Name)" +} + +output "service_url" { + value = google_cloud_run_v2_service.main.uri + description = "The URL on which the deployed service is available" +} + +output "project_id" { + value = google_cloud_run_v2_service.main.project + description = "Google Cloud project in which the service was created" +} + +output "location" { + value = google_cloud_run_v2_service.main.location + description = "Location in which the Cloud Run service was created" +} + +output "service_id" { + value = google_cloud_run_v2_service.main.id + description = "Unique Identifier for the created service" +} + +output "service_status" { + value = try(google_cloud_run_v2_service.main.terminal_condition[0].type, null) + description = "Status of the created service" +} + output "apphub_service_uri" { value = { service_uri = "//run.googleapis.com/${google_cloud_run_v2_service.main.id}" service_id = substr("${var.service_name}-${md5("google-cloud-run-v2-${var.location}-${var.project_id}")}", 0, 63) - location = var.location } description = "Service URI in CAIS style to be used by Apphub." } + +output "domain_map_id" { + value = values(google_cloud_run_domain_mapping.domain_map)[*].id + description = "Unique Identifier for the created domain map" +} + +output "domain_map_status" { + value = values(google_cloud_run_domain_mapping.domain_map)[*].status + description = "Status of Domain mapping" +} diff --git a/modules/v2/variables.tf b/modules/v2/variables.tf index c62537e7..ffec1059 100644 --- a/modules/v2/variables.tf +++ b/modules/v2/variables.tf @@ -357,3 +357,32 @@ variable "execution_environment" { } } +variable "verified_domain_name" { + description = "List of custom Domain Name." + type = list(string) + default = [] +} + +variable "certificate_mode" { + description = "The mode of the certificate (NONE or AUTOMATIC)." + type = string + default = "NONE" +} + +variable "force_override" { + description = "Option to force override existing mapping." + type = bool + default = false +} + +variable "domain_map_labels" { + description = "A set of key/value label pairs to assign to the Domain mapping." + type = map(string) + default = {} +} + +variable "domain_map_annotations" { + description = "Annotations to the domain map." + type = map(string) + default = {} +}