Skip to content

Commit 72ef715

Browse files
committed
feat: initial commit composer v3
1 parent ba0eb61 commit 72ef715

File tree

6 files changed

+689
-0
lines changed

6 files changed

+689
-0
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Module Cloud Composer Environment ([v3](https://cloud.google.com/composer/docs/composer-3/composer-overview))
2+
3+
This module is used to create a Cloud Composer v3 environment.
4+
5+
## Compatibility
6+
7+
This module is meant for use with Terraform 1.3+ and tested using Terraform 1.3+. If you find incompatibilities using Terraform >=1.3, please open an issue.
8+
9+
10+
```hcl
11+
module "simple-composer-environment" {
12+
source = "terraform-google-modules/composer/google//modules/create_environment_v3"
13+
version = "~> 7.0"
14+
project_id = var.project_id
15+
composer_env_name = "test-composer-env"
16+
region = "us-central1"
17+
composer_service_account = var.composer_service_account
18+
network = "test-vpc"
19+
subnetwork = "test-subnet"
20+
grant_sa_agent_permission = false
21+
environment_size = "ENVIRONMENT_SIZE_SMALL"
22+
use_private_environment = true
23+
composer_network_attachment_name = "composer-na"
24+
25+
scheduler = {
26+
cpu = 0.5
27+
memory_gb = 1.875
28+
storage_gb = 1
29+
count = 2
30+
}
31+
32+
dag_processor = {
33+
cpu = 0.5
34+
memory_gb = 1.875
35+
storage_gb = 1
36+
count = 2
37+
}
38+
39+
web_server = {
40+
cpu = 0.5
41+
memory_gb = 1.875
42+
storage_gb = 1
43+
}
44+
45+
worker = {
46+
cpu = 0.5
47+
memory_gb = 1.875
48+
storage_gb = 1
49+
min_count = 2
50+
max_count = 3
51+
}
52+
53+
triggerer = {
54+
cpu = 1
55+
memory_gb = 1
56+
count = 2
57+
}
58+
}
59+
60+
```
61+
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
62+
## Inputs
63+
64+
| Name | Description | Type | Default | Required |
65+
|------|-------------|------|---------|:--------:|
66+
| airflow\_config\_overrides | Airflow configuration properties to override. Property keys contain the section and property names, separated by a hyphen, for example "core-dags\_are\_paused\_at\_creation". | `map(string)` | `{}` | no |
67+
| cloud\_composer\_connection\_subnetwork | Subnetwork self-link. When specified, the environment will use Private Service Connect instead of VPC peerings to connect to CloudSQL in the Tenant Project. IP address of psc endpoint is allocated from this subnet | `string` | `null` | no |
68+
| cloud\_composer\_network\_ipv4\_cidr\_block | The CIDR block from which IP range in tenant project will be reserved. Required if VPC peering is used to connect to CloudSql instead of PSC | `string` | `null` | no |
69+
| cloud\_data\_lineage\_integration | Whether or not Dataplex data lineage integration is enabled. Cloud Composer environments in versions composer-2.1.2-airflow-..* and newer) | `bool` | `false` | no |
70+
| cloud\_sql\_ipv4\_cidr | The CIDR block from which IP range in tenant project will be reserved for Cloud SQL private service access. Required if VPC peering is used to connect to CloudSql instead of PSC | `string` | `null` | no |
71+
| composer\_env\_name | Name of Cloud Composer Environment | `string` | n/a | yes |
72+
| composer\_service\_account | Service Account for running Cloud Composer. | `string` | `null` | no |
73+
| enable\_ip\_masq\_agent | Deploys 'ip-masq-agent' daemon set in the GKE cluster and defines nonMasqueradeCIDRs equals to pod IP range so IP masquerading is used for all destination addresses, except between pods traffic. | `bool` | `false` | no |
74+
| enable\_private\_endpoint | Configure private access to the cluster endpoint. If true, access to the public endpoint of the GKE cluster is denied | `bool` | `false` | no |
75+
| enable\_privately\_used\_public\_ips | When enabled, IPs from public (non-RFC1918) ranges can be used for pod\_ip\_allocation\_range\_name and service\_ip\_allocation\_range\_name. | `bool` | `false` | no |
76+
| env\_variables | Variables of the airflow environment. | `map(string)` | `{}` | no |
77+
| environment\_size | The environment size controls the performance parameters of the managed Cloud Composer infrastructure that includes the Airflow database. Values for environment size are: `ENVIRONMENT_SIZE_SMALL`, `ENVIRONMENT_SIZE_MEDIUM`, and `ENVIRONMENT_SIZE_LARGE`. | `string` | `"ENVIRONMENT_SIZE_MEDIUM"` | no |
78+
| grant\_sa\_agent\_permission | Cloud Composer relies on Workload Identity as Google API authentication mechanism for Airflow. | `bool` | `true` | no |
79+
| image\_version | The version of the aiflow running in the cloud composer environment. | `string` | `"composer-2.10.2-airflow-2.10.2"` | no |
80+
| kms\_key\_name | Customer-managed Encryption Key fully qualified resource name, i.e. projects/project-id/locations/location/keyRings/keyring/cryptoKeys/key. | `string` | `null` | no |
81+
| labels | The resource labels (a map of key/value pairs) to be applied to the Cloud Composer. | `map(string)` | `{}` | no |
82+
| maintenance\_end\_time | Time window specified for recurring maintenance operations in RFC3339 format | `string` | `null` | no |
83+
| maintenance\_recurrence | Frequency of the recurring maintenance window in RFC5545 format. | `string` | `null` | no |
84+
| maintenance\_start\_time | Time window specified for daily or recurring maintenance operations in RFC3339 format | `string` | `"05:00"` | no |
85+
| master\_authorized\_networks | List of master authorized networks. If none are provided, disallow external access (except the cluster node IPs, which GKE automatically whitelists). | <pre>list(object({<br> cidr_block = string<br> display_name = string<br> }))</pre> | `[]` | no |
86+
| master\_ipv4\_cidr | The CIDR block from which IP range in tenant project will be reserved for the GKE master. Required when `use_private_environment` and `enable_private_endpoint` is `true` | `string` | `null` | no |
87+
| network | The VPC network to host the composer cluster. | `string` | n/a | yes |
88+
| network\_project\_id | The project ID of the shared VPC's host (for shared vpc support) | `string` | `""` | no |
89+
| pod\_ip\_allocation\_range\_name | The name of the subnet secondary range, used to allocate IP addresses for the pods. | `string` | `null` | no |
90+
| project\_id | Project ID where Cloud Composer Environment is created. | `string` | n/a | yes |
91+
| pypi\_packages | Custom Python Package Index (PyPI) packages to be installed in the environment. Keys refer to the lowercase package name (e.g. "numpy"). | `map(string)` | `{}` | no |
92+
| region | Region where the Cloud Composer Environment is created. | `string` | `"us-central1"` | no |
93+
| resilience\_mode | Cloud Composer 2.1.15 or newer only. The resilience mode states whether high resilience is enabled for the environment or not. Values for resilience mode are `HIGH_RESILIENCE` for high resilience and `STANDARD_RESILIENCE` for standard resilience | `string` | `null` | no |
94+
| scheduled\_snapshots\_config | The recovery configuration settings for the Cloud Composer environment | <pre>object({<br> enabled = optional(bool, false)<br> snapshot_location = optional(string)<br> snapshot_creation_schedule = optional(string)<br> time_zone = optional(string)<br> })</pre> | `null` | no |
95+
| scheduler | Configuration for resources used by Airflow schedulers. | <pre>object({<br> cpu = string<br> memory_gb = number<br> storage_gb = number<br> count = number<br> })</pre> | <pre>{<br> "count": 2,<br> "cpu": 2,<br> "memory_gb": 7.5,<br> "storage_gb": 5<br>}</pre> | no |
96+
| service\_ip\_allocation\_range\_name | The name of the subnet secondary range, used to allocate IP addresses for the Services. | `string` | `null` | no |
97+
| storage\_bucket | Name of an existing Cloud Storage bucket to be used by the environment | `string` | `null` | no |
98+
| subnetwork | The name of the subnetwork to host the composer cluster. | `string` | n/a | yes |
99+
| subnetwork\_region | The subnetwork region of the shared VPC's host (for shared vpc support) | `string` | `""` | no |
100+
| tags | Tags applied to all nodes. Tags are used to identify valid sources or targets for network firewalls. | `set(string)` | `[]` | no |
101+
| task\_logs\_retention\_storage\_mode | The mode of storage for Airflow workers task logs. Values for storage mode are CLOUD\_LOGGING\_ONLY to only store logs in cloud logging and CLOUD\_LOGGING\_AND\_CLOUD\_STORAGE to store logs in cloud logging and cloud storage. Cloud Composer 2.0.23 or newer only | `string` | `null` | no |
102+
| triggerer | Configuration for resources used by Airflow triggerer | <pre>object({<br> cpu = string<br> memory_gb = number<br> count = number<br> })</pre> | `null` | no |
103+
| use\_private\_environment | Create a private environment. | `bool` | `false` | no |
104+
| web\_server | Configuration for resources used by Airflow web server. | <pre>object({<br> cpu = string<br> memory_gb = number<br> storage_gb = number<br> })</pre> | <pre>{<br> "cpu": 2,<br> "memory_gb": 7.5,<br> "storage_gb": 5<br>}</pre> | no |
105+
| web\_server\_network\_access\_control | The network-level access control policy for the Airflow web server. If unspecified, no network-level access restrictions are applied | <pre>list(object({<br> allowed_ip_range = string<br> description = string<br> }))</pre> | `null` | no |
106+
| worker | Configuration for resources used by Airflow workers. | <pre>object({<br> cpu = string<br> memory_gb = number<br> storage_gb = number<br> min_count = number<br> max_count = number<br> })</pre> | <pre>{<br> "cpu": 2,<br> "max_count": 6,<br> "memory_gb": 7.5,<br> "min_count": 2,<br> "storage_gb": 5<br>}</pre> | no |
107+
108+
## Outputs
109+
110+
| Name | Description |
111+
|------|-------------|
112+
| airflow\_uri | URI of the Apache Airflow Web UI hosted within the Cloud Composer Environment. |
113+
| composer\_env | Cloud Composer Environment |
114+
| composer\_env\_id | ID of Cloud Composer Environment. |
115+
| composer\_env\_name | Name of the Cloud Composer Environment. |
116+
| gcs\_bucket | Google Cloud Storage bucket which hosts DAGs for the Cloud Composer Environment. |
117+
| gke\_cluster | Google Kubernetes Engine cluster used to run the Cloud Composer Environment. |
118+
119+
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
data "google_project" "project" {
18+
project_id = var.project_id
19+
}
20+
21+
resource "google_project_iam_member" "composer_agent_service_account" {
22+
count = var.grant_sa_agent_permission ? 1 : 0
23+
project = data.google_project.project.project_id
24+
role = "roles/composer.ServiceAgentV2Ext"
25+
member = format("serviceAccount:%s", local.cloud_composer_sa)
26+
}
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/**
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
locals {
18+
network_project_id = var.network_project_id != "" ? var.network_project_id : var.project_id
19+
subnetwork_region = var.subnetwork_region != "" ? var.subnetwork_region : var.region
20+
cloud_composer_sa = format("service-%[email protected]", data.google_project.project.number)
21+
}
22+
23+
resource "google_composer_environment" "composer_env" {
24+
provider = google-beta
25+
26+
project = var.project_id
27+
name = var.composer_env_name
28+
region = var.region
29+
labels = var.labels
30+
31+
dynamic "storage_config" {
32+
for_each = var.storage_bucket != null ? ["storage_config"] : []
33+
content {
34+
bucket = var.storage_bucket
35+
}
36+
}
37+
38+
config {
39+
40+
enable_private_environment = var.use_private_environment # reusing the existing variable name from previous versions
41+
42+
environment_size = var.environment_size
43+
resilience_mode = var.resilience_mode
44+
45+
node_config {
46+
service_account = var.composer_service_account
47+
tags = var.tags
48+
network = var.create_network_attachment ? "projects/${local.network_project_id}/global/networks/${var.network}" : null
49+
subnetwork = var.create_network_attachment ? "projects/${local.network_project_id}/regions/${local.subnetwork_region}/subnetworks/${var.subnetwork}" : null
50+
composer_network_attachment = var.create_network_attachment ? null : "projects/{var.project_id}/regions/${var.region}/networkAttachments/${var.composer_network_attachment_name}"
51+
}
52+
53+
dynamic "software_config" {
54+
for_each = [
55+
{
56+
airflow_config_overrides = var.airflow_config_overrides
57+
pypi_packages = var.pypi_packages
58+
env_variables = var.env_variables
59+
image_version = var.image_version
60+
web_server_plugins_mode = var.web_server_plugins_mode
61+
}]
62+
content {
63+
airflow_config_overrides = software_config.value["airflow_config_overrides"]
64+
pypi_packages = software_config.value["pypi_packages"]
65+
env_variables = software_config.value["env_variables"]
66+
image_version = software_config.value["image_version"]
67+
web_server_plugins_mode = software_config.value["web_server_plugins_mode"]
68+
dynamic "cloud_data_lineage_integration" {
69+
for_each = var.cloud_data_lineage_integration ? ["cloud_data_lineage_integration"] : []
70+
content {
71+
enabled = var.cloud_data_lineage_integration
72+
}
73+
}
74+
}
75+
}
76+
77+
dynamic "maintenance_window" {
78+
for_each = (var.maintenance_end_time != null && var.maintenance_recurrence != null) ? [
79+
{
80+
start_time = var.maintenance_start_time
81+
end_time = var.maintenance_end_time
82+
recurrence = var.maintenance_recurrence
83+
}] : []
84+
content {
85+
start_time = maintenance_window.value["start_time"]
86+
end_time = maintenance_window.value["end_time"]
87+
recurrence = maintenance_window.value["recurrence"]
88+
}
89+
}
90+
91+
workloads_config {
92+
93+
dynamic "scheduler" {
94+
for_each = var.scheduler != null ? [var.scheduler] : []
95+
content {
96+
cpu = scheduler.value["cpu"]
97+
memory_gb = scheduler.value["memory_gb"]
98+
storage_gb = scheduler.value["storage_gb"]
99+
count = scheduler.value["count"]
100+
}
101+
}
102+
103+
dynamic "web_server" {
104+
for_each = var.web_server != null ? [var.web_server] : []
105+
content {
106+
cpu = web_server.value["cpu"]
107+
memory_gb = web_server.value["memory_gb"]
108+
storage_gb = web_server.value["storage_gb"]
109+
}
110+
}
111+
112+
dynamic "worker" {
113+
for_each = var.worker != null ? [var.worker] : []
114+
content {
115+
cpu = worker.value["cpu"]
116+
memory_gb = worker.value["memory_gb"]
117+
storage_gb = worker.value["storage_gb"]
118+
min_count = worker.value["min_count"]
119+
max_count = worker.value["max_count"]
120+
}
121+
}
122+
123+
dynamic "triggerer" {
124+
for_each = var.triggerer != null ? [var.triggerer] : []
125+
content {
126+
cpu = triggerer.value["cpu"]
127+
memory_gb = triggerer.value["memory_gb"]
128+
count = triggerer.value["count"]
129+
}
130+
}
131+
132+
dynamic "dag_processor" {
133+
for_each = var.dag_processor != null ? [var.dag_processor] : []
134+
content {
135+
cpu = dag_processor.value["cpu"]
136+
memory_gb = dag_processor.value["memory_gb"]
137+
storage_gb = dag_processor.value["storage_gb"]
138+
count = dag_processor.value["count"]
139+
}
140+
}
141+
142+
}
143+
144+
dynamic "recovery_config" {
145+
for_each = var.scheduled_snapshots_config != null ? ["recovery_config"] : []
146+
content {
147+
dynamic "scheduled_snapshots_config" {
148+
for_each = var.scheduled_snapshots_config != null ? [var.scheduled_snapshots_config] : []
149+
content {
150+
enabled = scheduled_snapshots_config.value["enabled"]
151+
snapshot_location = scheduled_snapshots_config.value["snapshot_location"]
152+
snapshot_creation_schedule = scheduled_snapshots_config.value["snapshot_creation_schedule"]
153+
time_zone = scheduled_snapshots_config.value["time_zone"]
154+
}
155+
}
156+
}
157+
}
158+
159+
dynamic "web_server_network_access_control" {
160+
for_each = var.web_server_network_access_control == null ? [] : ["web_server_network_access_control"]
161+
content {
162+
dynamic "allowed_ip_range" {
163+
for_each = { for x in var.web_server_network_access_control : x.allowed_ip_range => x }
164+
content {
165+
value = allowed_ip_range.value["allowed_ip_range"]
166+
description = allowed_ip_range.value["description"]
167+
}
168+
}
169+
}
170+
}
171+
172+
dynamic "encryption_config" {
173+
for_each = var.kms_key_name != null ? ["encryption_config"] : []
174+
content {
175+
kms_key_name = var.kms_key_name
176+
}
177+
}
178+
179+
dynamic "data_retention_config" {
180+
for_each = var.task_logs_retention_storage_mode == null ? [] : ["data_retention_config"]
181+
content {
182+
task_logs_retention_config {
183+
storage_mode = var.task_logs_retention_storage_mode
184+
}
185+
}
186+
}
187+
}
188+
189+
depends_on = [google_project_iam_member.composer_agent_service_account]
190+
191+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
output "composer_env_name" {
18+
value = google_composer_environment.composer_env.name
19+
description = "Name of the Cloud Composer Environment."
20+
}
21+
22+
output "composer_env_id" {
23+
value = google_composer_environment.composer_env.id
24+
description = "ID of Cloud Composer Environment."
25+
}
26+
27+
output "gcs_bucket" {
28+
value = google_composer_environment.composer_env.config[0].dag_gcs_prefix
29+
description = "Google Cloud Storage bucket which hosts DAGs for the Cloud Composer Environment."
30+
}
31+
32+
output "airflow_uri" {
33+
value = google_composer_environment.composer_env.config[0].airflow_uri
34+
description = "URI of the Apache Airflow Web UI hosted within the Cloud Composer Environment."
35+
}
36+
37+
output "composer_env" {
38+
value = google_composer_environment.composer_env
39+
description = "Cloud Composer Environment"
40+
}

0 commit comments

Comments
 (0)