" # Enable url map resource.
6 |
--------------------------------------------------------------------------------
/examples/web_app_protection_example/main.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 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 | ## ---------------------------------------------------------------------------------------------------------------------
18 | ## NETWORKS
19 | ## Modules created for configuring networks used in two different regions...
20 | ## ---------------------------------------------------------------------------------------------------------------------
21 |
22 | locals {
23 | network_cfg = {
24 | "network1" = {
25 | network_name = "vpc-webapp-r1"
26 | cnat_region = "us-central1"
27 | subnets = [
28 | {
29 | subnet_name = "webapp-r1-subnet01"
30 | subnet_ip = "10.0.16.0/24"
31 | subnet_region = "us-central1"
32 | },
33 | {
34 | subnet_name = "webapp-r1-subnet02"
35 | subnet_ip = "10.0.18.0/24"
36 | subnet_region = "us-west1"
37 | },
38 | ]
39 | },
40 | "network2" = {
41 | network_name = "vpc-webapp-r2"
42 | cnat_region = "us-east1"
43 | subnets = [
44 | {
45 | subnet_name = "webapp-r2-subnet01"
46 | subnet_ip = "10.0.32.0/24"
47 | subnet_region = "us-east1"
48 | },
49 | {
50 | subnet_name = "webapp-r2-subnet02"
51 | subnet_ip = "10.0.34.0/24"
52 | subnet_region = "us-east4"
53 | },
54 | ]
55 | },
56 | }
57 | }
58 |
59 | module "network" {
60 | source = "GoogleCloudPlatform/waap/google//modules/mig-network"
61 | version = "~> 0.1"
62 |
63 | for_each = local.network_cfg
64 |
65 | project_id = var.project_id
66 |
67 | region = each.value.cnat_region
68 | network_name = each.value.network_name
69 | subnets = each.value.subnets
70 | }
71 |
72 | ## ---------------------------------------------------------------------------------------------------------------------
73 | ## MIGs
74 | ## Creation of templates and configuration of MIGs.
75 | ## ---------------------------------------------------------------------------------------------------------------------
76 |
77 | ## Configuration for each Managed Instance Group
78 | locals {
79 | mig_cfg = {
80 | "mig01" = {
81 | machine_type = "e2-small"
82 | source_image = "debian-11"
83 | source_image_project = "debian-cloud"
84 | disk_size = "50"
85 |
86 | startup_script = file("./scripts/startup-script.sh")
87 |
88 | mig_name = "mig-01"
89 | region = "us-central1"
90 |
91 | target_size = 2
92 | max_surge_fixed = 4
93 | max_unavailable_fixed = 0
94 |
95 | port_name = "http"
96 | backend_port = 80
97 |
98 | network = module.network["network1"].network_name
99 | subnetwork = module.network["network1"].subnets[0]
100 |
101 | service_account = "sa-mig-01"
102 | roles = ["roles/monitoring.metricWriter", "roles/logging.logWriter"]
103 | scopes = ["logging-write", "monitoring-write", "cloud-platform"]
104 |
105 | tags = ["mig-01", "lb-web-hc"]
106 | },
107 | "mig02" = {
108 | machine_type = "e2-small"
109 | source_image = "debian-11"
110 | source_image_project = "debian-cloud"
111 | disk_size = "50"
112 |
113 | startup_script = file("./scripts/startup-script.sh")
114 |
115 | mig_name = "mig-02"
116 | region = "us-east1"
117 |
118 | target_size = 2
119 | max_surge_fixed = 3
120 | max_unavailable_fixed = 0
121 |
122 | port_name = "http"
123 | backend_port = 80
124 |
125 | network = module.network["network2"].network_name
126 | subnetwork = module.network["network2"].subnets[0]
127 |
128 | service_account = "sa-mig-02"
129 | roles = ["roles/monitoring.metricWriter", "roles/logging.logWriter"]
130 | scopes = ["logging-write", "monitoring-write", "cloud-platform"]
131 |
132 | tags = ["mig-02", "lb-web-hc"]
133 | },
134 | # Add more settings for other MIGs if needed
135 | }
136 | }
137 | module "mig" {
138 | source = "GoogleCloudPlatform/waap/google//modules/mig"
139 | version = "~> 0.1"
140 | for_each = local.mig_cfg
141 |
142 | project_id = var.project_id
143 |
144 | machine_type = each.value.machine_type
145 | source_image = each.value.source_image
146 | source_image_project = each.value.source_image_project
147 | disk_size_gb = each.value.disk_size
148 |
149 | startup_script = each.value.startup_script
150 |
151 | mig_name = each.value.mig_name
152 | region = each.value.region
153 |
154 | target_size = each.value.target_size
155 | max_surge_fixed = each.value.max_surge_fixed
156 | max_unavailable_fixed = each.value.max_unavailable_fixed
157 |
158 | port_name = each.value.port_name
159 | backend_port = each.value.backend_port
160 |
161 | network = each.value.network
162 | subnetwork = each.value.subnetwork
163 |
164 | service_account = each.value.service_account
165 | roles = each.value.roles
166 | scopes = each.value.scopes
167 |
168 | tags = each.value.tags
169 | }
170 |
171 | ## ---------------------------------------------------------------------------------------------------------------------
172 | ## RECAPTCHA
173 | ## Score Recaptcha Configuration.
174 | ## ---------------------------------------------------------------------------------------------------------------------
175 |
176 | resource "google_recaptcha_enterprise_key" "primary" {
177 | display_name = "web_recaptcha"
178 | project = var.project_id
179 |
180 | testing_options {
181 | testing_score = 0.5
182 | }
183 |
184 | web_settings {
185 | integration_type = "SCORE"
186 | allow_all_domains = true
187 | allow_amp_traffic = false
188 | }
189 | }
190 |
191 | resource "random_id" "suffix" {
192 | byte_length = 4
193 | }
194 |
195 | ## ---------------------------------------------------------------------------------------------------------------------
196 | ## CLOUD ARMOR
197 | ## Backend Policy configuration with owasp rules.
198 | ## ---------------------------------------------------------------------------------------------------------------------
199 |
200 | resource "google_compute_security_policy" "edge_policy" {
201 | project = var.project_id
202 | name = "edge-policy-${random_id.suffix.hex}"
203 | type = "CLOUD_ARMOR_EDGE"
204 | description = "Edge Security Policy"
205 |
206 | rule {
207 | action = "allow"
208 | description = "Default rule, higher priority overrides it"
209 | priority = 2147483647
210 | match {
211 | versioned_expr = "SRC_IPS_V1"
212 | config {
213 | src_ip_ranges = ["*"]
214 | }
215 | }
216 | }
217 |
218 | rule {
219 | action = "deny(403)"
220 | description = "Deny Specific IP address"
221 | priority = 7000
222 |
223 | match {
224 | versioned_expr = "SRC_IPS_V1"
225 | config {
226 | src_ip_ranges = ["85.172.66.254/32"]
227 | }
228 | }
229 | }
230 |
231 | rule {
232 | action = "deny(403)"
233 | priority = 7005
234 | description = "Deny Specific Region"
235 | match {
236 | expr {
237 | expression = "origin.region_code == 'CH'"
238 | }
239 | }
240 | }
241 | lifecycle {
242 | create_before_destroy = true
243 | }
244 | }
245 |
246 | module "backend_policy" {
247 | source = "GoogleCloudPlatform/cloud-armor/google"
248 | version = "0.3.0"
249 |
250 | project_id = var.project_id
251 | name = "backend-policy-${random_id.suffix.hex}"
252 | description = "Backend Security Policy"
253 | default_rule_action = "allow"
254 | type = "CLOUD_ARMOR"
255 | layer_7_ddos_defense_enable = true
256 | layer_7_ddos_defense_rule_visibility = "STANDARD"
257 |
258 | recaptcha_redirect_site_key = google_recaptcha_enterprise_key.primary.name
259 |
260 | pre_configured_rules = {
261 | "sqli_sensitivity_level_1" = {
262 | action = "deny(403)"
263 | priority = 9000
264 | description = "Block SQL Injection"
265 | target_rule_set = "sqli-v33-stable"
266 | rate_limit_options = {
267 | rate_limit_http_request_count = 100
268 | rate_limit_http_request_interval_sec = 10
269 | ban_duration_sec = 60
270 | }
271 | }
272 |
273 | "xss-stable_level_1" = {
274 | action = "deny(403)"
275 | priority = 9005
276 | description = "Block XSS"
277 | target_rule_set = "xss-v33-stable"
278 | sensitivity_level = 1
279 | rate_limit_options = {
280 | rate_limit_http_request_count = 100
281 | rate_limit_http_request_interval_sec = 10
282 | ban_duration_sec = 60
283 | }
284 | }
285 |
286 | "lfi-stable_level_1" = {
287 | action = "deny(403)"
288 | priority = 9010
289 | description = "Block Local File Inclusion"
290 | target_rule_set = "lfi-v33-stable"
291 | sensitivity_level = 1
292 | rate_limit_options = {
293 | rate_limit_http_request_count = 100
294 | rate_limit_http_request_interval_sec = 10
295 | ban_duration_sec = 60
296 | }
297 | }
298 |
299 | "rfi-stable_level_1" = {
300 | action = "deny(403)"
301 | priority = 9015
302 | description = "Block Remote File Inclusion"
303 | target_rule_set = "rfi-v33-stable"
304 | sensitivity_level = 1
305 | rate_limit_options = {
306 | rate_limit_http_request_count = 100
307 | rate_limit_http_request_interval_sec = 10
308 | ban_duration_sec = 60
309 | }
310 | }
311 |
312 | "methodenforcement-stable_level_1" = {
313 | action = "deny(403)"
314 | priority = 9020
315 | description = "Block Method Enforcement"
316 | target_rule_set = "methodenforcement-v33-stable"
317 | sensitivity_level = 1
318 | rate_limit_options = {
319 | rate_limit_http_request_count = 100
320 | rate_limit_http_request_interval_sec = 10
321 | ban_duration_sec = 60
322 | }
323 | }
324 |
325 | "rce-stable_level_1" = {
326 | action = "deny(403)"
327 | priority = 9025
328 | description = "Block Remote Code Execution"
329 | target_rule_set = "rce-v33-stable"
330 | sensitivity_level = 1
331 | rate_limit_options = {
332 | rate_limit_http_request_count = 100
333 | rate_limit_http_request_interval_sec = 10
334 | ban_duration_sec = 60
335 | }
336 | }
337 |
338 | "protocolattack-stable_level_1" = {
339 | action = "deny(403)"
340 | priority = 9030
341 | description = "Block Protocol Attack"
342 | target_rule_set = "protocolattack-v33-stable"
343 | sensitivity_level = 1
344 | rate_limit_options = {
345 | rate_limit_http_request_count = 100
346 | rate_limit_http_request_interval_sec = 10
347 | ban_duration_sec = 60
348 | }
349 | }
350 |
351 | "scannerdetection-stable_level_1" = {
352 | action = "deny(403)"
353 | priority = 9035
354 | description = "Block Scanner Detection"
355 | target_rule_set = "scannerdetection-v33-stable"
356 | sensitivity_level = 1
357 | rate_limit_options = {
358 | rate_limit_http_request_count = 100
359 | rate_limit_http_request_interval_sec = 10
360 | ban_duration_sec = 60
361 | }
362 | }
363 |
364 | "php-stable_level_1" = {
365 | action = "deny(403)"
366 | priority = 9040
367 | description = "Block PHP Injection Attack"
368 | target_rule_set = "php-v33-stable"
369 | sensitivity_level = 1
370 | rate_limit_options = {
371 | rate_limit_http_request_count = 100
372 | rate_limit_http_request_interval_sec = 10
373 | ban_duration_sec = 60
374 | }
375 | }
376 |
377 | "sessionfixation-stable_level_1" = {
378 | action = "deny(403)"
379 | priority = 9045
380 | description = "Block Session Fixation Attack"
381 | target_rule_set = "sessionfixation-v33-stable"
382 | sensitivity_level = 1
383 | rate_limit_options = {
384 | rate_limit_http_request_count = 100
385 | rate_limit_http_request_interval_sec = 10
386 | ban_duration_sec = 60
387 | }
388 | }
389 |
390 | "java-stable_level_1" = {
391 | action = "deny(403)"
392 | priority = 9050
393 | description = "Block Java Attack"
394 | target_rule_set = "java-v33-stable"
395 | sensitivity_level = 1
396 | rate_limit_options = {
397 | rate_limit_http_request_count = 100
398 | rate_limit_http_request_interval_sec = 10
399 | ban_duration_sec = 60
400 | }
401 | }
402 |
403 | "nodejs-stable_level_1" = {
404 | action = "deny(403)"
405 | priority = 9055
406 | description = "Block NodeJS Attack"
407 | target_rule_set = "nodejs-v33-stable"
408 | sensitivity_level = 1
409 | rate_limit_options = {
410 | rate_limit_http_request_count = 100
411 | rate_limit_http_request_interval_sec = 10
412 | ban_duration_sec = 60
413 | }
414 | }
415 |
416 | "cve-canary_level_1" = {
417 | action = "deny(403)"
418 | priority = 9060
419 | description = "Fix Log4j Vulnerability"
420 | target_rule_set = "cve-canary"
421 | sensitivity_level = 1
422 | rate_limit_options = {
423 | rate_limit_http_request_count = 100
424 | rate_limit_http_request_interval_sec = 10
425 | ban_duration_sec = 60
426 | }
427 | }
428 |
429 | "json-sqli-canary_level_2" = {
430 | action = "deny(403)"
431 | priority = 9065
432 | description = "JSON-based SQL injection bypass vulnerability"
433 | target_rule_set = "json-sqli-canary"
434 | sensitivity_level = 2
435 | rate_limit_options = {
436 | rate_limit_http_request_count = 100
437 | rate_limit_http_request_interval_sec = 10
438 | ban_duration_sec = 60
439 | }
440 | }
441 | }
442 | }
443 |
444 | ## ---------------------------------------------------------------------------------------------------------------------
445 | ## LOAD BALANCER
446 | ## Configuration of the Load Balancer and its resources.
447 | ## ---------------------------------------------------------------------------------------------------------------------
448 |
449 | locals {
450 | health_check = {
451 | check_interval_sec = 60
452 | timeout_sec = 60
453 | healthy_threshold = 2
454 | unhealthy_threshold = 2
455 | request_path = "/"
456 | port = 80
457 | host = null
458 | logging = false
459 | }
460 |
461 | }
462 | module "lb-http" {
463 | source = "GoogleCloudPlatform/lb-http/google"
464 | version = "9.0.0"
465 |
466 | project = var.project_id
467 | name = "lb-web-app"
468 | target_tags = ["lb-web-hc"]
469 |
470 | load_balancing_scheme = "EXTERNAL_MANAGED"
471 |
472 | firewall_networks = [module.network["network1"].network_name, module.network["network2"].network_name]
473 | firewall_projects = [var.project_id, var.project_id]
474 | use_ssl_certificates = false
475 | ssl = false
476 | https_redirect = false
477 | quic = true
478 |
479 | create_url_map = var.url_map ? false : true
480 | url_map = try(google_compute_url_map.traffic_mgmt[0].self_link, null)
481 |
482 | backends = {
483 | default = {
484 |
485 | description = "Web App Default Backend"
486 | protocol = "HTTP"
487 | port = 80
488 | port_name = "http"
489 | timeout_sec = 600
490 | enable_cdn = true
491 | connection_draining_timeout_sec = null
492 | compression_mode = "AUTOMATIC"
493 | security_policy = module.backend_policy.policy.name
494 | edge_security_policy = google_compute_security_policy.edge_policy.id
495 | session_affinity = null
496 | affinity_cookie_ttl_sec = null
497 | custom_request_headers = null
498 | custom_response_headers = null
499 |
500 | health_check = local.health_check
501 | log_config = {
502 | enable = true
503 | sample_rate = 0.05
504 | }
505 |
506 | cdn_policy = {
507 | cache_mode = "CACHE_ALL_STATIC"
508 | default_ttl = 3600
509 | client_ttl = 1800
510 | max_ttl = 28800
511 | serve_while_stale = 86400
512 | negative_caching = true
513 |
514 | negative_caching_policy = {
515 | code = 404
516 | ttl = 60
517 | }
518 |
519 | cache_key_policy = {
520 | include_host = true
521 | include_protocol = true
522 | include_query_string = true
523 | include_named_cookies = ["__next_preview_data", "__prerender_bypass"]
524 | }
525 | }
526 |
527 | groups = [
528 | {
529 | group = module.mig["mig01"].instance_group
530 | balancing_mode = "UTILIZATION"
531 | capacity_scaler = null
532 | description = null
533 | max_connections = null
534 | max_connections_per_instance = null
535 | max_connections_per_endpoint = null
536 | max_rate = null
537 | max_rate_per_instance = null
538 | max_rate_per_endpoint = null
539 | max_utilization = 0.9
540 | },
541 | {
542 | group = module.mig["mig02"].instance_group
543 | balancing_mode = "UTILIZATION"
544 | capacity_scaler = null
545 | description = null
546 | max_connections = null
547 | max_connections_per_instance = null
548 | max_connections_per_endpoint = null
549 | max_rate = null
550 | max_rate_per_instance = null
551 | max_rate_per_endpoint = null
552 | max_utilization = 0.9
553 | },
554 | ]
555 |
556 | iap_config = {
557 | enable = false
558 | oauth2_client_id = ""
559 | oauth2_client_secret = ""
560 | }
561 | }
562 | web-app01 = {
563 |
564 | description = "Web App Backend 01"
565 | protocol = "HTTP"
566 | port = 80
567 | port_name = "http"
568 | timeout_sec = 600
569 | enable_cdn = true
570 | connection_draining_timeout_sec = null
571 | compression_mode = "AUTOMATIC"
572 | security_policy = module.backend_policy.policy.name
573 | edge_security_policy = google_compute_security_policy.edge_policy.id
574 | session_affinity = null
575 | affinity_cookie_ttl_sec = null
576 | custom_request_headers = null
577 | custom_response_headers = null
578 |
579 | health_check = local.health_check
580 | log_config = {
581 | enable = true
582 | sample_rate = 0.05
583 | }
584 |
585 | cdn_policy = {
586 | cache_mode = "CACHE_ALL_STATIC"
587 | default_ttl = 3600
588 | client_ttl = 1800
589 | max_ttl = 28800
590 | serve_while_stale = 86400
591 | negative_caching = true
592 |
593 | negative_caching_policy = {
594 | code = 404
595 | ttl = 60
596 | }
597 |
598 | cache_key_policy = {
599 | include_host = true
600 | include_protocol = true
601 | include_query_string = true
602 | include_named_cookies = ["__next_preview_data", "__prerender_bypass"]
603 | }
604 | }
605 |
606 | groups = [
607 | {
608 | group = module.mig["mig01"].instance_group
609 | balancing_mode = "UTILIZATION"
610 | capacity_scaler = null
611 | description = null
612 | max_connections = null
613 | max_connections_per_instance = null
614 | max_connections_per_endpoint = null
615 | max_rate = null
616 | max_rate_per_instance = null
617 | max_rate_per_endpoint = null
618 | max_utilization = 0.9
619 | },
620 | ]
621 | iap_config = {
622 | enable = false
623 | oauth2_client_id = null
624 | oauth2_client_secret = null
625 | }
626 | }
627 |
628 | web-app02 = {
629 |
630 | description = "Web App Backend 02"
631 | protocol = "HTTP"
632 | port = 80
633 | port_name = "http"
634 | timeout_sec = 600
635 | enable_cdn = true
636 | connection_draining_timeout_sec = null
637 | compression_mode = "AUTOMATIC"
638 | security_policy = module.backend_policy.policy.name
639 | edge_security_policy = google_compute_security_policy.edge_policy.id
640 | session_affinity = null
641 | affinity_cookie_ttl_sec = null
642 | custom_request_headers = null
643 | custom_response_headers = null
644 |
645 | health_check = local.health_check
646 |
647 | log_config = {
648 | enable = true
649 | sample_rate = 0.05
650 | }
651 |
652 | cdn_policy = {
653 | cache_mode = "CACHE_ALL_STATIC"
654 | default_ttl = 3600
655 | client_ttl = 1800
656 | max_ttl = 28800
657 | serve_while_stale = 86400
658 | negative_caching = true
659 |
660 | negative_caching_policy = {
661 | code = 404
662 | ttl = 60
663 | }
664 |
665 | cache_key_policy = {
666 | include_host = true
667 | include_protocol = true
668 | include_query_string = true
669 | include_named_cookies = ["__next_preview_data", "__prerender_bypass"]
670 | }
671 | }
672 |
673 | groups = [
674 | {
675 | group = module.mig["mig02"].instance_group
676 | balancing_mode = "UTILIZATION"
677 | capacity_scaler = null
678 | description = null
679 | max_connections = null
680 | max_connections_per_instance = null
681 | max_connections_per_endpoint = null
682 | max_rate = null
683 | max_rate_per_instance = null
684 | max_rate_per_endpoint = null
685 | max_utilization = 0.9
686 | },
687 | ]
688 | iap_config = {
689 | enable = false
690 | oauth2_client_id = null
691 | oauth2_client_secret = null
692 | }
693 | }
694 | }
695 | }
696 |
697 | resource "google_compute_url_map" "traffic_mgmt" {
698 | count = var.url_map ? 1 : 0
699 |
700 | project = var.project_id
701 |
702 | name = "lb-web-app"
703 | description = "UrlMap used to route requests to a backend service based on rules."
704 | default_service = module.lb-http.backend_services["default"].self_link
705 |
706 | host_rule {
707 | hosts = ["*"]
708 | path_matcher = "allpaths"
709 | }
710 |
711 | path_matcher {
712 | name = "allpaths"
713 | default_service = module.lb-http.backend_services["default"].self_link
714 |
715 | path_rule {
716 | paths = ["/"]
717 | route_action {
718 | weighted_backend_services {
719 | backend_service = module.lb-http.backend_services["web-app01"].self_link
720 | weight = 400
721 | }
722 | weighted_backend_services {
723 | backend_service = module.lb-http.backend_services["web-app02"].self_link
724 | weight = 600
725 | }
726 | }
727 | }
728 | }
729 | }
730 |
731 | ## ---------------------------------------------------------------------------------------------------------------------
732 | ## MONITORING
733 | ## Dashboard
734 | ## ---------------------------------------------------------------------------------------------------------------------
735 |
736 | resource "google_monitoring_dashboard" "dashboard" {
737 | dashboard_json = file("./scripts/dashboard.json")
738 | project = var.project_id
739 |
740 | lifecycle {
741 | ignore_changes = [
742 | dashboard_json
743 | ]
744 | }
745 |
746 | }
747 |
748 | locals {
749 | policies = {
750 | "sql_injection" = "jsonPayload.enforcedSecurityPolicy.preconfiguredExprIds=~\"sqli\" AND jsonPayload.enforcedSecurityPolicy.preconfiguredExprIds!=\"owasp-crs-id942550-sqli\""
751 | "cross_site_scripting" = "jsonPayload.enforcedSecurityPolicy.preconfiguredExprIds=~\"xss\""
752 | "local_file_inclusion" = "jsonPayload.enforcedSecurityPolicy.preconfiguredExprIds=~\"lfi\""
753 | "remote_code_execution" = "jsonPayload.enforcedSecurityPolicy.preconfiguredExprIds=~\"rce\""
754 | "remote_file_inclusion" = "jsonPayload.enforcedSecurityPolicy.preconfiguredExprIds=~\"rfi\""
755 | "method_enforcement" = "jsonPayload.enforcedSecurityPolicy.preconfiguredExprIds=~\"methodenforcement\""
756 | "scanner_detection" = "jsonPayload.enforcedSecurityPolicy.preconfiguredExprIds=~\"scannerdetection\""
757 | "protocol_attack" = "jsonPayload.enforcedSecurityPolicy.preconfiguredExprIds=~\"protocolattack\""
758 | "php_injection_attack" = "jsonPayload.enforcedSecurityPolicy.preconfiguredExprIds=~\"php\""
759 | "session_fixation_attack" = "jsonPayload.enforcedSecurityPolicy.preconfiguredExprIds=~\"sessionfixation\""
760 | "java_attack" = "jsonPayload.enforcedSecurityPolicy.preconfiguredExprIds=~\"java\""
761 | "nodejs_attack" = "jsonPayload.enforcedSecurityPolicy.preconfiguredExprIds=~\"nodejs\""
762 | "log4j_attack" = "jsonPayload.enforcedSecurityPolicy.preconfiguredExprIds=~\"cve\""
763 | "json_sql_injection" = "jsonPayload.enforcedSecurityPolicy.preconfiguredExprIds=~\"owasp-crs-id942550-sqli\""
764 | }
765 | }
766 |
767 | resource "google_logging_metric" "logging_metric" {
768 | for_each = local.policies
769 | project = var.project_id
770 |
771 | name = "${each.key}/metric"
772 | filter = each.value
773 | metric_descriptor {
774 | metric_kind = "DELTA"
775 | value_type = "INT64"
776 |
777 | labels {
778 | key = "signature_id"
779 | value_type = "STRING"
780 | }
781 | }
782 | label_extractors = {
783 | "signature_id" = "EXTRACT(jsonPayload.enforcedSecurityPolicy.preconfiguredExprIds)"
784 | }
785 | }
786 |
--------------------------------------------------------------------------------
/examples/web_app_protection_example/scripts/README.md:
--------------------------------------------------------------------------------
1 | # Startup scripts folder
2 |
3 | In this folder we have the startup scripts used to configure the instances that are the backends of the solution.
4 |
5 | The content of the scripts listed in this folder is a literal representation of the startup-script of a Compute Instance. Therefore, if you want to reproduce the settings defined for the startup script of a backend that is already running in the GCP environment, just copy the content of the startup script of the desired instance and replace the content of the startup-script.sh file located on this folder.
6 |
7 | This action, if performed before deploying the environment, will cause the managed instance groups templates to be created already using the desired startup script settings.
8 |
9 | If the action is performed after the environment has already gone through the first deployment process, this action will recreate the Managed Instance Group template file and will trigger the Managed Instance Group to replace the current instances with new instances using the new template.
10 |
11 | ### * Note that in the example we used, we automated not only the process of deploying the instance with its basic configurations, but also added the configurations related to our example application.
12 |
13 | As we are using Managed Instance Groups, we recommend that a similar automation process be performed for the deployment of your application using the startup script.
14 |
15 |
--------------------------------------------------------------------------------
/examples/web_app_protection_example/scripts/dashboard.json:
--------------------------------------------------------------------------------
1 | {
2 | "displayName": "Web App Protection Dashboard",
3 | "mosaicLayout": {
4 | "columns": 12,
5 | "tiles": [
6 | {
7 | "height": 4,
8 | "widget": {
9 | "title": "Backend Latency",
10 | "xyChart": {
11 | "chartOptions": {},
12 | "dataSets": [
13 | {
14 | "plotType": "LINE",
15 | "targetAxis": "Y1",
16 | "timeSeriesQuery": {
17 | "timeSeriesQueryLanguage": "fetch https_lb_rule::loadbalancing.googleapis.com/https/backend_latencies\n| align delta()\n| group_by [resource.backend_target_name, resource.url_map_name],\n [value_backend_latencies_aggregate: aggregate(value.backend_latencies)]\n| value\n [value_backend_latencies_aggregate_percentile_from:\n percentile_from(value_backend_latencies_aggregate, 95)]"
18 | }
19 | }
20 | ],
21 | "timeshiftDuration": "0s",
22 | "yAxis": {
23 | "scale": "LINEAR"
24 | }
25 | }
26 | },
27 | "width": 6,
28 | "xPos": 6,
29 | "yPos": 4
30 | },
31 | {
32 | "height": 4,
33 | "widget": {
34 | "title": "Total latency",
35 | "xyChart": {
36 | "chartOptions": {},
37 | "dataSets": [
38 | {
39 | "plotType": "LINE",
40 | "targetAxis": "Y1",
41 | "timeSeriesQuery": {
42 | "timeSeriesQueryLanguage": "fetch https_lb_rule\n| metric 'loadbalancing.googleapis.com/https/total_latencies'\n| group_by 1m,\n [value_total_latencies_percentile: percentile(value.total_latencies, 99)]\n| every 1m\n| group_by [resource.url_map_name, resource.project_id],\n [value_total_latencies_percentile_aggregate:\n aggregate(value_total_latencies_percentile)]"
43 | }
44 | }
45 | ],
46 | "timeshiftDuration": "0s",
47 | "yAxis": {
48 | "scale": "LINEAR"
49 | }
50 | }
51 | },
52 | "width": 6,
53 | "xPos": 6
54 | },
55 | {
56 | "height": 4,
57 | "widget": {
58 | "title": "Frontend RTT",
59 | "xyChart": {
60 | "chartOptions": {},
61 | "dataSets": [
62 | {
63 | "minAlignmentPeriod": "60s",
64 | "plotType": "LINE",
65 | "targetAxis": "Y1",
66 | "timeSeriesQuery": {
67 | "timeSeriesFilter": {
68 | "aggregation": {
69 | "alignmentPeriod": "60s",
70 | "crossSeriesReducer": "REDUCE_PERCENTILE_50",
71 | "groupByFields": [
72 | "resource.label.\"url_map_name\""
73 | ],
74 | "perSeriesAligner": "ALIGN_DELTA"
75 | },
76 | "filter": "metric.type=\"loadbalancing.googleapis.com/https/frontend_tcp_rtt\" resource.type=\"https_lb_rule\""
77 | }
78 | }
79 | }
80 | ],
81 | "timeshiftDuration": "0s",
82 | "yAxis": {
83 | "scale": "LINEAR"
84 | }
85 | }
86 | },
87 | "width": 3,
88 | "xPos": 3,
89 | "yPos": 4
90 | },
91 | {
92 | "height": 4,
93 | "widget": {
94 | "title": " 5XX Response Ratio",
95 | "xyChart": {
96 | "chartOptions": {},
97 | "dataSets": [
98 | {
99 | "plotType": "LINE",
100 | "targetAxis": "Y1",
101 | "timeSeriesQuery": {
102 | "timeSeriesQueryLanguage": "fetch https_lb_rule::loadbalancing.googleapis.com/https/request_count\n| align delta()\n| group_by [resource.url_map_name, resource.project_id],\n [c'metric response code class = if sum /':\n sum(if(metric.response_code_class = 500, value.request_count, 0))\n / sum(value.request_count)]\n| value\n [c'metric response code class = if sum / scale':\n scale(c'metric response code class = if sum /', '%')]"
103 | }
104 | }
105 | ],
106 | "timeshiftDuration": "0s",
107 | "yAxis": {
108 | "scale": "LINEAR"
109 | }
110 | }
111 | },
112 | "width": 3,
113 | "yPos": 4
114 | },
115 | {
116 | "height": 3,
117 | "widget": {
118 | "title": "Client Response Code",
119 | "xyChart": {
120 | "chartOptions": {},
121 | "dataSets": [
122 | {
123 | "plotType": "STACKED_AREA",
124 | "targetAxis": "Y1",
125 | "timeSeriesQuery": {
126 | "timeSeriesQueryLanguage": "fetch https_lb_rule\n| metric 'loadbalancing.googleapis.com/https/request_count'\n| group_by 1h, [row_count: row_count()]\n| every 1h\n| group_by [metric.response_code_class],\n [row_count_aggregate: aggregate(row_count)]"
127 | }
128 | }
129 | ],
130 | "timeshiftDuration": "0s",
131 | "yAxis": {
132 | "scale": "LINEAR"
133 | }
134 | }
135 | },
136 | "width": 3,
137 | "xPos": 6,
138 | "yPos": 8
139 | },
140 | {
141 | "height": 2,
142 | "widget": {
143 | "scorecard": {
144 | "timeSeriesQuery": {
145 | "timeSeriesFilter": {
146 | "aggregation": {
147 | "alignmentPeriod": "60s",
148 | "crossSeriesReducer": "REDUCE_SUM",
149 | "perSeriesAligner": "ALIGN_SUM"
150 | },
151 | "filter": "metric.type=\"loadbalancing.googleapis.com/https/response_bytes_count\" resource.type=\"https_lb_rule\""
152 | }
153 | }
154 | },
155 | "title": "Total Egress"
156 | },
157 | "width": 2,
158 | "yPos": 2
159 | },
160 | {
161 | "height": 2,
162 | "widget": {
163 | "scorecard": {
164 | "timeSeriesQuery": {
165 | "timeSeriesFilter": {
166 | "aggregation": {
167 | "alignmentPeriod": "60s",
168 | "crossSeriesReducer": "REDUCE_SUM",
169 | "perSeriesAligner": "ALIGN_SUM"
170 | },
171 | "filter": "metric.type=\"loadbalancing.googleapis.com/https/response_bytes_count\" resource.type=\"https_lb_rule\" metric.label.\"cache_result\"=\"MISS\""
172 | }
173 | }
174 | },
175 | "title": "Cache Miss Egress"
176 | },
177 | "width": 2,
178 | "xPos": 4,
179 | "yPos": 2
180 | },
181 | {
182 | "height": 3,
183 | "widget": {
184 | "title": "Request Count by Country",
185 | "xyChart": {
186 | "chartOptions": {},
187 | "dataSets": [
188 | {
189 | "plotType": "STACKED_AREA",
190 | "targetAxis": "Y1",
191 | "timeSeriesQuery": {
192 | "timeSeriesQueryLanguage": "fetch https_lb_rule\n| metric 'loadbalancing.googleapis.com/https/request_count'\n| group_by 1h, [row_count: row_count()]\n| every 1h\n| group_by [metric.client_country], [row_count_aggregate: aggregate(row_count)]"
193 | }
194 | }
195 | ],
196 | "timeshiftDuration": "0s",
197 | "yAxis": {
198 | "scale": "LINEAR"
199 | }
200 | }
201 | },
202 | "width": 3,
203 | "xPos": 9,
204 | "yPos": 8
205 | },
206 | {
207 | "height": 2,
208 | "widget": {
209 | "scorecard": {
210 | "gaugeView": {
211 | "upperBound": 1.0
212 | },
213 | "timeSeriesQuery": {
214 | "timeSeriesQueryLanguage": "fetch https_lb_rule\n| metric 'loadbalancing.googleapis.com/https/request_count'\n| filter\n (metric.response_code_class == 500)\n| group_by 1m, [value_request_count_aggregate: aggregate(value.request_count)]\n| every 1m\n| group_by [],\n [value_request_count_aggregate_aggregate:\n aggregate(value_request_count_aggregate)]"
215 | }
216 | },
217 | "title": "5xx Errors"
218 | },
219 | "width": 2,
220 | "xPos": 4
221 | },
222 | {
223 | "height": 2,
224 | "widget": {
225 | "scorecard": {
226 | "sparkChartView": {
227 | "sparkChartType": "SPARK_LINE"
228 | },
229 | "timeSeriesQuery": {
230 | "timeSeriesQueryLanguage": "fetch https_lb_rule\n| metric 'loadbalancing.googleapis.com/https/total_latencies'\n| group_by 1m,\n [value_total_latencies_percentile: percentile(value.total_latencies, 99)]\n| every 1m\n| group_by [resource.url_map_name, resource.project_id],\n [value_total_latencies_percentile_aggregate:\n aggregate(value_total_latencies_percentile)]"
231 | }
232 | },
233 | "title": "Total Latency"
234 | },
235 | "width": 2
236 | },
237 | {
238 | "height": 2,
239 | "widget": {
240 | "scorecard": {
241 | "timeSeriesQuery": {
242 | "timeSeriesQueryLanguage": "fetch https_lb_rule\n| metric 'loadbalancing.googleapis.com/https/request_count'\n| group_by 1m, [value_request_count_aggregate: aggregate(value.request_count)]\n| every 1m\n| group_by [],\n [value_request_count_aggregate_aggregate:\n aggregate(value_request_count_aggregate)]"
243 | }
244 | },
245 | "title": "Total Requests"
246 | },
247 | "width": 2,
248 | "xPos": 2
249 | },
250 | {
251 | "height": 2,
252 | "widget": {
253 | "scorecard": {
254 | "timeSeriesQuery": {
255 | "timeSeriesQueryLanguage": "fetch https_lb_rule\n| metric 'loadbalancing.googleapis.com/https/response_bytes_count'\n| filter (metric.cache_result == 'HIT')\n| group_by 1m,\n [value_response_bytes_count_aggregate:\n aggregate(value.response_bytes_count)]\n| every 1m\n| group_by [],\n [value_response_bytes_count_aggregate_aggregate:\n aggregate(value_response_bytes_count_aggregate)]"
256 | }
257 | },
258 | "title": "Cache Hit Egress"
259 | },
260 | "width": 2,
261 | "xPos": 2,
262 | "yPos": 2
263 | },
264 | {
265 | "height": 3,
266 | "widget": {
267 | "title": "Requests (Total) by Policy Name",
268 | "xyChart": {
269 | "chartOptions": {},
270 | "dataSets": [
271 | {
272 | "minAlignmentPeriod": "60s",
273 | "plotType": "STACKED_AREA",
274 | "targetAxis": "Y1",
275 | "timeSeriesQuery": {
276 | "timeSeriesFilter": {
277 | "aggregation": {
278 | "alignmentPeriod": "60s",
279 | "crossSeriesReducer": "REDUCE_SUM",
280 | "groupByFields": [
281 | "resource.label.\"policy_name\""
282 | ],
283 | "perSeriesAligner": "ALIGN_SUM"
284 | },
285 | "filter": "metric.type=\"networksecurity.googleapis.com/https/request_count\" resource.type=\"network_security_policy\"",
286 | "secondaryAggregation": {
287 | "alignmentPeriod": "60s"
288 | }
289 | }
290 | }
291 | }
292 | ],
293 | "timeshiftDuration": "0s",
294 | "yAxis": {
295 | "scale": "LINEAR"
296 | }
297 | }
298 | },
299 | "width": 3,
300 | "yPos": 8
301 | },
302 | {
303 | "height": 3,
304 | "widget": {
305 | "title": "Requests (Blocked) by Policy Name",
306 | "xyChart": {
307 | "chartOptions": {},
308 | "dataSets": [
309 | {
310 | "minAlignmentPeriod": "60s",
311 | "plotType": "STACKED_AREA",
312 | "targetAxis": "Y1",
313 | "timeSeriesQuery": {
314 | "timeSeriesFilter": {
315 | "aggregation": {
316 | "alignmentPeriod": "60s",
317 | "crossSeriesReducer": "REDUCE_SUM",
318 | "groupByFields": [
319 | "resource.label.\"policy_name\""
320 | ],
321 | "perSeriesAligner": "ALIGN_SUM"
322 | },
323 | "filter": "metric.type=\"networksecurity.googleapis.com/https/request_count\" resource.type=\"network_security_policy\" metric.label.\"blocked\"=\"true\"",
324 | "secondaryAggregation": {
325 | "alignmentPeriod": "60s"
326 | }
327 | }
328 | }
329 | }
330 | ],
331 | "timeshiftDuration": "0s",
332 | "yAxis": {
333 | "scale": "LINEAR"
334 | }
335 | }
336 | },
337 | "width": 3,
338 | "xPos": 3,
339 | "yPos": 8
340 | },
341 | {
342 | "height": 4,
343 | "widget": {
344 | "title": "CRS 3.0",
345 | "xyChart": {
346 | "chartOptions": {},
347 | "dataSets": [
348 | {
349 | "minAlignmentPeriod": "60s",
350 | "plotType": "LINE",
351 | "targetAxis": "Y1",
352 | "timeSeriesQuery": {
353 | "timeSeriesFilter": {
354 | "aggregation": {
355 | "alignmentPeriod": "60s",
356 | "crossSeriesReducer": "REDUCE_SUM",
357 | "perSeriesAligner": "ALIGN_MEAN"
358 | },
359 | "filter": "metric.type=\"logging.googleapis.com/user/cross_site_scripting/metric\" resource.type=\"global\"",
360 | "secondaryAggregation": {
361 | "alignmentPeriod": "60s"
362 | }
363 | }
364 | }
365 | },
366 | {
367 | "minAlignmentPeriod": "60s",
368 | "plotType": "LINE",
369 | "targetAxis": "Y1",
370 | "timeSeriesQuery": {
371 | "timeSeriesFilter": {
372 | "aggregation": {
373 | "alignmentPeriod": "60s",
374 | "crossSeriesReducer": "REDUCE_SUM",
375 | "groupByFields": [
376 | "metric.label.\"signature_id\""
377 | ],
378 | "perSeriesAligner": "ALIGN_MEAN"
379 | },
380 | "filter": "metric.type=\"logging.googleapis.com/user/local_file_inclusion/metric\" resource.type=\"global\"",
381 | "secondaryAggregation": {
382 | "alignmentPeriod": "60s"
383 | }
384 | }
385 | }
386 | },
387 | {
388 | "minAlignmentPeriod": "60s",
389 | "plotType": "LINE",
390 | "targetAxis": "Y1",
391 | "timeSeriesQuery": {
392 | "timeSeriesFilter": {
393 | "aggregation": {
394 | "alignmentPeriod": "60s",
395 | "crossSeriesReducer": "REDUCE_SUM",
396 | "perSeriesAligner": "ALIGN_MEAN"
397 | },
398 | "filter": "metric.type=\"logging.googleapis.com/user/method_enforcement/metric\" resource.type=\"global\"",
399 | "secondaryAggregation": {
400 | "alignmentPeriod": "60s"
401 | }
402 | }
403 | }
404 | },
405 | {
406 | "minAlignmentPeriod": "60s",
407 | "plotType": "LINE",
408 | "targetAxis": "Y1",
409 | "timeSeriesQuery": {
410 | "timeSeriesFilter": {
411 | "aggregation": {
412 | "alignmentPeriod": "60s",
413 | "crossSeriesReducer": "REDUCE_SUM",
414 | "perSeriesAligner": "ALIGN_MEAN"
415 | },
416 | "filter": "metric.type=\"logging.googleapis.com/user/php_injection_attack/metric\" resource.type=\"global\"",
417 | "secondaryAggregation": {
418 | "alignmentPeriod": "60s"
419 | }
420 | }
421 | }
422 | },
423 | {
424 | "minAlignmentPeriod": "60s",
425 | "plotType": "LINE",
426 | "targetAxis": "Y1",
427 | "timeSeriesQuery": {
428 | "timeSeriesFilter": {
429 | "aggregation": {
430 | "alignmentPeriod": "60s",
431 | "crossSeriesReducer": "REDUCE_SUM",
432 | "perSeriesAligner": "ALIGN_MEAN"
433 | },
434 | "filter": "metric.type=\"logging.googleapis.com/user/protocol_attack/metric\" resource.type=\"global\"",
435 | "secondaryAggregation": {
436 | "alignmentPeriod": "60s"
437 | }
438 | }
439 | }
440 | },
441 | {
442 | "minAlignmentPeriod": "60s",
443 | "plotType": "LINE",
444 | "targetAxis": "Y1",
445 | "timeSeriesQuery": {
446 | "timeSeriesFilter": {
447 | "aggregation": {
448 | "alignmentPeriod": "60s",
449 | "crossSeriesReducer": "REDUCE_SUM",
450 | "perSeriesAligner": "ALIGN_MEAN"
451 | },
452 | "filter": "metric.type=\"logging.googleapis.com/user/remote_code_execution/metric\" resource.type=\"global\"",
453 | "secondaryAggregation": {
454 | "alignmentPeriod": "60s"
455 | }
456 | }
457 | }
458 | },
459 | {
460 | "minAlignmentPeriod": "60s",
461 | "plotType": "LINE",
462 | "targetAxis": "Y1",
463 | "timeSeriesQuery": {
464 | "timeSeriesFilter": {
465 | "aggregation": {
466 | "alignmentPeriod": "60s",
467 | "crossSeriesReducer": "REDUCE_SUM",
468 | "perSeriesAligner": "ALIGN_MEAN"
469 | },
470 | "filter": "metric.type=\"logging.googleapis.com/user/remote_file_inclusion/metric\" resource.type=\"global\"",
471 | "secondaryAggregation": {
472 | "alignmentPeriod": "60s"
473 | }
474 | }
475 | }
476 | },
477 | {
478 | "minAlignmentPeriod": "60s",
479 | "plotType": "LINE",
480 | "targetAxis": "Y1",
481 | "timeSeriesQuery": {
482 | "timeSeriesFilter": {
483 | "aggregation": {
484 | "alignmentPeriod": "60s",
485 | "crossSeriesReducer": "REDUCE_SUM",
486 | "perSeriesAligner": "ALIGN_MEAN"
487 | },
488 | "filter": "metric.type=\"logging.googleapis.com/user/scanner_detection/metric\" resource.type=\"global\"",
489 | "secondaryAggregation": {
490 | "alignmentPeriod": "60s"
491 | }
492 | }
493 | }
494 | },
495 | {
496 | "minAlignmentPeriod": "60s",
497 | "plotType": "LINE",
498 | "targetAxis": "Y1",
499 | "timeSeriesQuery": {
500 | "timeSeriesFilter": {
501 | "aggregation": {
502 | "alignmentPeriod": "60s",
503 | "crossSeriesReducer": "REDUCE_SUM",
504 | "perSeriesAligner": "ALIGN_MEAN"
505 | },
506 | "filter": "metric.type=\"logging.googleapis.com/user/session_fixation_attack/metric\" resource.type=\"global\"",
507 | "secondaryAggregation": {
508 | "alignmentPeriod": "60s"
509 | }
510 | }
511 | }
512 | },
513 | {
514 | "minAlignmentPeriod": "60s",
515 | "plotType": "LINE",
516 | "targetAxis": "Y1",
517 | "timeSeriesQuery": {
518 | "timeSeriesFilter": {
519 | "aggregation": {
520 | "alignmentPeriod": "60s",
521 | "crossSeriesReducer": "REDUCE_SUM",
522 | "groupByFields": [
523 | "metric.label.\"signature_id\""
524 | ],
525 | "perSeriesAligner": "ALIGN_MEAN"
526 | },
527 | "filter": "metric.type=\"logging.googleapis.com/user/sql_injection/metric\"",
528 | "secondaryAggregation": {
529 | "alignmentPeriod": "60s"
530 | }
531 | }
532 | }
533 | }
534 | ],
535 | "timeshiftDuration": "0s",
536 | "yAxis": {
537 | "scale": "LINEAR"
538 | }
539 | }
540 | },
541 | "width": 6,
542 | "yPos": 11
543 | },
544 | {
545 | "height": 4,
546 | "widget": {
547 | "title": "CRS 3.3 & Complex Rules",
548 | "xyChart": {
549 | "chartOptions": {},
550 | "dataSets": [
551 | {
552 | "minAlignmentPeriod": "60s",
553 | "plotType": "LINE",
554 | "targetAxis": "Y1",
555 | "timeSeriesQuery": {
556 | "timeSeriesFilter": {
557 | "aggregation": {
558 | "alignmentPeriod": "60s",
559 | "crossSeriesReducer": "REDUCE_SUM",
560 | "groupByFields": [
561 | "metric.label.\"signature_id\""
562 | ],
563 | "perSeriesAligner": "ALIGN_MEAN"
564 | },
565 | "filter": "metric.type=\"logging.googleapis.com/user/java_attack/metric\" resource.type=\"global\""
566 | }
567 | }
568 | },
569 | {
570 | "minAlignmentPeriod": "60s",
571 | "plotType": "LINE",
572 | "targetAxis": "Y1",
573 | "timeSeriesQuery": {
574 | "timeSeriesFilter": {
575 | "aggregation": {
576 | "alignmentPeriod": "60s",
577 | "crossSeriesReducer": "REDUCE_SUM",
578 | "groupByFields": [
579 | "metric.label.\"signature_id\""
580 | ],
581 | "perSeriesAligner": "ALIGN_MEAN"
582 | },
583 | "filter": "metric.type=\"logging.googleapis.com/user/nodejs_attack/metric\" resource.type=\"global\""
584 | }
585 | }
586 | },
587 | {
588 | "minAlignmentPeriod": "60s",
589 | "plotType": "LINE",
590 | "targetAxis": "Y1",
591 | "timeSeriesQuery": {
592 | "timeSeriesFilter": {
593 | "aggregation": {
594 | "alignmentPeriod": "60s",
595 | "crossSeriesReducer": "REDUCE_SUM",
596 | "groupByFields": [
597 | "metric.label.\"signature_id\""
598 | ],
599 | "perSeriesAligner": "ALIGN_MEAN"
600 | },
601 | "filter": "metric.type=\"logging.googleapis.com/user/log4j_attack/metric\" resource.type=\"global\""
602 | }
603 | }
604 | },
605 | {
606 | "minAlignmentPeriod": "60s",
607 | "plotType": "LINE",
608 | "targetAxis": "Y1",
609 | "timeSeriesQuery": {
610 | "timeSeriesFilter": {
611 | "aggregation": {
612 | "alignmentPeriod": "60s",
613 | "crossSeriesReducer": "REDUCE_SUM",
614 | "groupByFields": [
615 | "metric.label.\"signature_id\""
616 | ],
617 | "perSeriesAligner": "ALIGN_MEAN"
618 | },
619 | "filter": "metric.type=\"logging.googleapis.com/user/json_sql_injection/metric\" resource.type=\"global\""
620 | }
621 | }
622 | }
623 | ],
624 | "yAxis": {
625 | "scale": "LINEAR"
626 | }
627 | }
628 | },
629 | "width": 6,
630 | "xPos": 6,
631 | "yPos": 11
632 | }
633 | ]
634 | }
635 | }
636 |
--------------------------------------------------------------------------------
/examples/web_app_protection_example/scripts/startup-script.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Copyright 2022 Google LLC
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | set -x
18 | curl -sSO https://dl.google.com/cloudagents/add-google-cloud-ops-agent-repo.sh
19 | sudo bash add-google-cloud-ops-agent-repo.sh --also-install
20 | ## Install docker and run the juice shop application.
21 | sudo apt-get -y install ca-certificates curl gnupg lsb-release
22 | sudo mkdir -m 0755 -p /etc/apt/keyrings
23 | curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
24 | echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
25 | $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
26 | apt-get update
27 | sudo apt-get -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
28 | ## Code to deploy Juice Shop
29 | docker pull bkimminich/juice-shop
30 | docker run -d -p 80:3000 bkimminich/juice-shop
31 |
32 | ## Code to deploy Hello World
33 | # docker pull crccheck/hello-world
34 | # docker run -d -p 80:8000 crccheck/hello-world
35 |
--------------------------------------------------------------------------------
/examples/web_app_protection_example/variables.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 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 | variable "project_id" {
18 | description = "Google Project ID in which the resources will be created."
19 | type = string
20 | }
21 |
22 | variable "url_map" {
23 | description = "Enable or disable the 'google_compute_url_map' feature to route requests to backends based on rules."
24 | type = bool
25 | default = false
26 | }
27 |
--------------------------------------------------------------------------------
/examples/web_app_protection_example/versions.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 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 | terraform {
17 | required_version = ">= 1.3"
18 | required_providers {
19 | google = {
20 | source = "hashicorp/google"
21 | version = ">= 3.45"
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/modules/apigee/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## Inputs
3 |
4 | | Name | Description | Type | Default | Required |
5 | |------|-------------|------|---------|:--------:|
6 | | analytics\_region | GCP region for storing Apigee analytics data (see https://cloud.google.com/apigee/docs/api-platform/get-started/install-cli). | `string` | n/a | yes |
7 | | apigee\_endpoint\_attachments | Apigee endpoint attachments (for southbound networking: https://cloud.google.com/apigee/docs/api-platform/architecture/southbound-networking-patterns-endpoints#create-the-psc-attachments). | map(object({
region = string
service_attachment = string
}))
| `{}` | no |
8 | | apigee\_envgroups | Apigee groups (NAME => [HOSTNAMES]). | `map(list(string))` | `null` | no |
9 | | apigee\_environments | Apigee Environments. | map(object({
display_name = optional(string)
description = optional(string, "Terraform-managed")
deployment_type = optional(string)
api_proxy_type = optional(string)
node_config = optional(object({
min_node_count = optional(number)
max_node_count = optional(number)
}))
iam = optional(map(list(string)))
envgroups = optional(list(string))
regions = optional(list(string))
}))
| `null` | no |
10 | | apigee\_instances | Apigee Instances ([REGION] => [INSTANCE]). | map(object({
display_name = optional(string)
description = optional(string, "Terraform-managed")
runtime_ip_cidr_range = string
troubleshooting_ip_cidr_range = string
disk_encryption_key = optional(string)
consumer_accept_list = optional(list(string))
}))
| `null` | no |
11 | | apigee\_org\_description | Description for Apigee Organization. | `string` | `"Apigee Org"` | no |
12 | | apigee\_org\_name | Display name for Apigee Organization. | `string` | `"Apigee Org"` | no |
13 | | billing\_type | Apigee billing type. Can be one of EVALUATION, PAYG, or SUBSCRIPTION. See https://cloud.google.com/apigee/pricing | `string` | `"EVALUATION"` | no |
14 | | create\_apigee\_org | Set to `true` to create a new Apigee org in the provided `var.project_id`; set to `false` to use the existing Apigee org in this project. | `bool` | `true` | no |
15 | | external\_ip | Reserved global external IP for Apigee Load Balancer | `string` | n/a | yes |
16 | | kms\_project\_id | Project ID in which to create keys for Apigee database and disk (org/instance) | `string` | `""` | no |
17 | | network\_id | VPC network ID | `string` | n/a | yes |
18 | | prevent\_key\_destroy | Prevent destroying KMS keys for Apigee Org and Instances | `bool` | `true` | no |
19 | | project\_id | Project id (also used for the Apigee Organization). | `string` | n/a | yes |
20 | | psa\_ranges | Apigee Private Service Access peering ranges | object({
apigee-range = string
google-managed-services-support-1 = string
})
| {
"apigee-range": "10.0.0.0/22",
"google-managed-services-support-1": "10.1.0.0/28"
}
| no |
21 | | runtime\_type | Apigee runtime type. Can be one of CLOUD or HYBRID. | `string` | `"CLOUD"` | no |
22 | | ssl\_certificate | SSL Certificate ID for Apigee Load Balancer | `string` | n/a | yes |
23 | | subnet\_id | Apigee NEG subnet ID | `string` | n/a | yes |
24 |
25 | ## Outputs
26 |
27 | | Name | Description |
28 | |------|-------------|
29 | | apigee\_org\_id | Apigee org ID (same as GCP project ID) |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/modules/apigee/main.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 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 | resource "google_project_service_identity" "apigee_sa" {
18 | provider = google-beta
19 | project = var.project_id
20 | service = "apigee.googleapis.com"
21 | }
22 |
23 | module "apigee" {
24 | source = "github.com/GoogleCloudPlatform/cloud-foundation-fabric//modules/apigee?ref=v24.0.0"
25 | project_id = var.project_id
26 | organization = !var.create_apigee_org ? null : {
27 | display_name = var.apigee_org_name
28 | description = var.apigee_org_description
29 | authorized_network = var.network_id
30 | runtime_type = var.runtime_type
31 | billing_type = var.billing_type
32 | database_encryption_key = module.apigee_org_kms.keys["org-db"]
33 | analytics_region = var.analytics_region
34 | }
35 | envgroups = var.apigee_envgroups
36 | environments = var.apigee_environments
37 | instances = { for k, v in var.apigee_instances : k => {
38 | display_name = v.display_name
39 | description = v.description
40 | runtime_ip_cidr_range = v.runtime_ip_cidr_range
41 | troubleshooting_ip_cidr_range = v.troubleshooting_ip_cidr_range
42 | consumer_accept_list = v.consumer_accept_list
43 | disk_encryption_key = module.apigee_instance_kms[k].keys["inst-disk"]
44 | } }
45 | endpoint_attachments = var.apigee_endpoint_attachments
46 | depends_on = [google_service_networking_connection.apigee_peering]
47 | }
48 |
49 | module "apigee_org_kms" {
50 | source = "terraform-google-modules/kms/google"
51 | version = "~> 2.2.1"
52 |
53 | project_id = var.kms_project_id == "" ? var.project_id : var.kms_project_id
54 | location = var.analytics_region
55 | keyring = "apigee-${var.project_id}"
56 | keys = ["org-db"]
57 | set_decrypters_for = ["org-db"]
58 | set_encrypters_for = ["org-db"]
59 | decrypters = [
60 | "serviceAccount:${google_project_service_identity.apigee_sa.email}"
61 | ]
62 | encrypters = [
63 | "serviceAccount:${google_project_service_identity.apigee_sa.email}"
64 | ]
65 | prevent_destroy = var.prevent_key_destroy
66 | }
67 |
68 | module "apigee_instance_kms" {
69 | for_each = var.apigee_instances
70 | source = "terraform-google-modules/kms/google"
71 | version = "~> 2.2.1"
72 |
73 | project_id = var.kms_project_id == "" ? var.project_id : var.kms_project_id
74 | location = each.key
75 | keyring = "apigee-${var.project_id}-inst-${each.key}"
76 | keys = ["inst-disk"]
77 | set_decrypters_for = ["inst-disk"]
78 | set_encrypters_for = ["inst-disk"]
79 | decrypters = [
80 | "serviceAccount:${google_project_service_identity.apigee_sa.email}"
81 | ]
82 | encrypters = [
83 | "serviceAccount:${google_project_service_identity.apigee_sa.email}"
84 | ]
85 | prevent_destroy = var.prevent_key_destroy
86 | }
87 |
88 | # Service Networking
89 | # https://cloud.google.com/apigee/docs/api-platform/get-started/install-cli#service-networking
90 | resource "google_compute_global_address" "apigee_ranges" {
91 | for_each = var.psa_ranges
92 | project = var.project_id
93 | name = each.key
94 | purpose = "VPC_PEERING"
95 | address_type = "INTERNAL"
96 | address = split("/", each.value)[0]
97 | prefix_length = split("/", each.value)[1]
98 | network = var.network_id
99 | }
100 |
101 | resource "google_service_networking_connection" "apigee_peering" {
102 | network = var.network_id
103 | service = "servicenetworking.googleapis.com"
104 | reserved_peering_ranges = [
105 | for k, v in google_compute_global_address.apigee_ranges : v.name
106 | ]
107 | }
108 |
109 | resource "google_compute_network_peering_routes_config" "psa_routes" {
110 | project = var.project_id
111 | peering = google_service_networking_connection.apigee_peering.peering
112 | network = split("/", var.network_id)[4] # grab network name from ID in format projects/{{project}}/global/networks/{{name}}
113 | export_custom_routes = false
114 | import_custom_routes = false
115 | }
116 |
117 | # Routing
118 | # https://cloud.google.com/apigee/docs/api-platform/get-started/install-cli#configure-routing
119 | resource "google_compute_region_network_endpoint_group" "psc_neg" {
120 | project = var.project_id
121 | for_each = var.apigee_instances
122 | name = "apigee-psc-neg-${each.key}"
123 | region = each.key
124 | network = var.network_id
125 | subnetwork = var.subnet_id
126 | network_endpoint_type = "PRIVATE_SERVICE_CONNECT"
127 | psc_target_service = module.apigee.service_attachments[each.key]
128 | lifecycle {
129 | create_before_destroy = true
130 | }
131 | }
132 |
133 | module "psc_lb" {
134 | source = "github.com/apigee/terraform-modules//modules/nb-psc-l7xlb?ref=v0.12.0"
135 |
136 | project_id = var.project_id
137 | name = "apigee-xlb-psc"
138 | network = var.network_id
139 | # psc_service_attachments = { (local.region) = module.apigee_core.instance_service_attachments[local.region] }
140 | ssl_certificate = var.ssl_certificate
141 | external_ip = var.external_ip
142 | psc_negs = [for _, psc_neg in google_compute_region_network_endpoint_group.psc_neg : psc_neg.id]
143 | }
144 |
--------------------------------------------------------------------------------
/modules/apigee/outputs.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 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 "apigee_org_id" {
18 | value = split("/", module.apigee.org_id)[1]
19 | description = "Apigee org ID (same as GCP project ID)"
20 | }
21 |
--------------------------------------------------------------------------------
/modules/apigee/variables.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 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 | variable "apigee_org_name" {
18 | description = "Display name for Apigee Organization."
19 | type = string
20 | default = "Apigee Org"
21 | }
22 |
23 | variable "apigee_org_description" {
24 | description = "Description for Apigee Organization."
25 | type = string
26 | default = "Apigee Org"
27 | }
28 |
29 | variable "analytics_region" {
30 | description = "GCP region for storing Apigee analytics data (see https://cloud.google.com/apigee/docs/api-platform/get-started/install-cli)."
31 | type = string
32 | }
33 |
34 | variable "apigee_envgroups" {
35 | description = "Apigee groups (NAME => [HOSTNAMES])."
36 | type = map(list(string))
37 | default = null
38 | }
39 |
40 | variable "apigee_environments" {
41 | description = "Apigee Environments."
42 | type = map(object({
43 | display_name = optional(string)
44 | description = optional(string, "Terraform-managed")
45 | deployment_type = optional(string)
46 | api_proxy_type = optional(string)
47 | node_config = optional(object({
48 | min_node_count = optional(number)
49 | max_node_count = optional(number)
50 | }))
51 | iam = optional(map(list(string)))
52 | envgroups = optional(list(string))
53 | regions = optional(list(string))
54 | }))
55 | default = null
56 | }
57 |
58 | variable "apigee_instances" {
59 | description = "Apigee Instances ([REGION] => [INSTANCE])."
60 | type = map(object({
61 | display_name = optional(string)
62 | description = optional(string, "Terraform-managed")
63 | runtime_ip_cidr_range = string
64 | troubleshooting_ip_cidr_range = string
65 | disk_encryption_key = optional(string)
66 | consumer_accept_list = optional(list(string))
67 | }))
68 | default = null
69 | }
70 |
71 | variable "apigee_endpoint_attachments" {
72 | description = "Apigee endpoint attachments (for southbound networking: https://cloud.google.com/apigee/docs/api-platform/architecture/southbound-networking-patterns-endpoints#create-the-psc-attachments)."
73 | type = map(object({
74 | region = string
75 | service_attachment = string
76 | }))
77 | default = {}
78 | }
79 |
80 | variable "kms_project_id" {
81 | description = "Project ID in which to create keys for Apigee database and disk (org/instance)"
82 | type = string
83 | default = ""
84 | }
85 |
86 | variable "psa_ranges" {
87 | description = "Apigee Private Service Access peering ranges"
88 | type = object({
89 | apigee-range = string
90 | google-managed-services-support-1 = string
91 | })
92 | default = {
93 | apigee-range = "10.0.0.0/22"
94 | google-managed-services-support-1 = "10.1.0.0/28"
95 | }
96 | }
97 |
98 | variable "project_id" {
99 | description = "Project id (also used for the Apigee Organization)."
100 | type = string
101 | }
102 |
103 | variable "network_id" {
104 | description = "VPC network ID"
105 | type = string
106 | }
107 |
108 | variable "subnet_id" {
109 | description = "Apigee NEG subnet ID"
110 | type = string
111 | }
112 |
113 | variable "ssl_certificate" {
114 | description = "SSL Certificate ID for Apigee Load Balancer"
115 | type = string
116 | }
117 |
118 | variable "external_ip" {
119 | description = "Reserved global external IP for Apigee Load Balancer"
120 | type = string
121 | }
122 |
123 | variable "billing_type" {
124 | description = "Apigee billing type. Can be one of EVALUATION, PAYG, or SUBSCRIPTION. See https://cloud.google.com/apigee/pricing"
125 | type = string
126 | default = "EVALUATION"
127 | }
128 |
129 | variable "runtime_type" {
130 | description = "Apigee runtime type. Can be one of CLOUD or HYBRID."
131 | type = string
132 | default = "CLOUD"
133 | }
134 |
135 | # variable "region" {
136 | # description = "Region in which to create resources"
137 | # type = string
138 | # default = "us-central1"
139 | # }
140 |
141 | variable "create_apigee_org" {
142 | description = "Set to `true` to create a new Apigee org in the provided `var.project_id`; set to `false` to use the existing Apigee org in this project."
143 | type = bool
144 | default = true
145 | }
146 |
147 | variable "prevent_key_destroy" {
148 | description = "Prevent destroying KMS keys for Apigee Org and Instances"
149 | type = bool
150 | default = true
151 | }
152 |
--------------------------------------------------------------------------------
/modules/apigee/versions.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 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 | terraform {
18 | required_version = ">= 1.3"
19 | required_providers {
20 | google = {
21 | source = "hashicorp/google"
22 | version = ">= 3.45"
23 | }
24 | google-beta = {
25 | source = "hashicorp/google-beta"
26 | version = ">= 3.45"
27 | }
28 | }
29 |
30 | provider_meta "google" {
31 | module_name = "blueprints/terraform/terraform-google-waap:apigee/v0.1.0"
32 | }
33 | provider_meta "google-beta" {
34 | module_name = "blueprints/terraform/terraform-google-waap:apigee/v0.1.0"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/modules/mig-network/README.md:
--------------------------------------------------------------------------------
1 | # MIG Network submodule
2 |
3 |
4 | ## Inputs
5 |
6 | | Name | Description | Type | Default | Required |
7 | |------|-------------|------|---------|:--------:|
8 | | network\_name | VPC network name | `string` | `""` | no |
9 | | project\_id | Google Project ID | `string` | `""` | no |
10 | | region | Region in which to create resources | `string` | `""` | no |
11 | | subnets | List of subnet configurations | list(object({
subnet_name = string
subnet_ip = string
subnet_region = string
}))
| n/a | yes |
12 |
13 | ## Outputs
14 |
15 | | Name | Description |
16 | |------|-------------|
17 | | network\_name | The name of the VPC being created |
18 | | subnets | List of created subnets |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/modules/mig-network/main.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 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 | /*****************
18 | *** Create VPC ***
19 | ******************/
20 | module "mig_vpc" {
21 | source = "terraform-google-modules/network/google"
22 | version = "~> 6.0"
23 |
24 | project_id = var.project_id
25 | network_name = var.network_name
26 | routing_mode = "GLOBAL"
27 |
28 | subnets = var.subnets
29 | }
30 |
31 | /***********************
32 | *** Create Cloud NAT ***
33 | ************************/
34 |
35 | module "cloud-nat" {
36 | source = "terraform-google-modules/cloud-nat/google"
37 | version = "~> 1.2"
38 | create_router = true
39 | project_id = var.project_id
40 | region = var.region
41 | network = module.mig_vpc.network_name
42 | router = format("router-%s", var.network_name)
43 | name = format("nat-%s", var.network_name)
44 | }
45 |
46 | /*********************************************************************************
47 | **** Firewall rule to allow incoming ssh connections from Google IAP servers. ****
48 | **********************************************************************************/
49 | resource "google_compute_firewall" "inbound-ip-ssh" {
50 | name = format("allow-ssh-iap-%s", var.network_name)
51 | project = var.project_id
52 | network = module.mig_vpc.network_name
53 |
54 | direction = "INGRESS"
55 | allow {
56 | protocol = "tcp"
57 | ports = ["22"]
58 | }
59 | source_ranges = [
60 | "35.235.240.0/20"
61 | ]
62 | target_tags = ["allow-ssh-iap"]
63 | }
64 |
--------------------------------------------------------------------------------
/modules/mig-network/outputs.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 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 | #!TODO
18 | output "network_name" {
19 | description = "The name of the VPC being created"
20 | value = module.mig_vpc.network_name
21 | }
22 |
23 | output "subnets" {
24 | description = "List of created subnets"
25 | value = module.mig_vpc.subnets_names
26 | }
27 |
--------------------------------------------------------------------------------
/modules/mig-network/variables.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 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 | variable "project_id" {
18 | description = "Google Project ID"
19 | type = string
20 | default = ""
21 | }
22 |
23 | variable "region" {
24 | description = "Region in which to create resources"
25 | type = string
26 | default = ""
27 | }
28 |
29 | variable "network_name" {
30 | description = "VPC network name"
31 | type = string
32 | default = ""
33 | }
34 |
35 | variable "subnets" {
36 | description = "List of subnet configurations"
37 | type = list(object({
38 | subnet_name = string
39 | subnet_ip = string
40 | subnet_region = string
41 | }))
42 | }
43 |
--------------------------------------------------------------------------------
/modules/mig-network/versions.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 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 | terraform {
18 | required_version = ">= 0.13.0"
19 | required_providers {
20 | google = {
21 | source = "hashicorp/google"
22 | version = ">= 3.45"
23 | }
24 | }
25 | provider_meta "google" {
26 | module_name = "blueprints/terraform/terraform-google-waap:mig-network/v0.1.0"
27 | }
28 | provider_meta "google-beta" {
29 | module_name = "blueprints/terraform/terraform-google-waap:mig-network/v0.1.0"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/modules/mig/README.md:
--------------------------------------------------------------------------------
1 | # MIG submodule
2 |
3 |
4 | ## Inputs
5 |
6 | | Name | Description | Type | Default | Required |
7 | |------|-------------|------|---------|:--------:|
8 | | backend\_port | The backend port number. | `number` | `80` | no |
9 | | disk\_auto\_delete | Whether or not the disk should be auto-deleted. | `bool` | `true` | no |
10 | | disk\_size\_gb | The size of the image in gigabytes. If not specified, it will inherit the size of its base image. | `string` | `"100"` | no |
11 | | disk\_type | The GCE disk type. Can be either pd-ssd, local-ssd, pd-balanced or pd-standard. | `string` | `"pd-standard"` | no |
12 | | machine\_type | Machine type to create, e.g. n1-standard-1 | `string` | `"n1-standard-1"` | no |
13 | | max\_surge\_fixed | The maximum number of instances that can be created above the specified targetSize during the update process. | `number` | n/a | yes |
14 | | max\_unavailable\_fixed | The maximum number of instances that can be unavailable during the update process. | `number` | n/a | yes |
15 | | mig\_name | Name of the managed instance group. | `string` | `""` | no |
16 | | name\_prefix | Name prefix for the instance template | `string` | `"vm-template-"` | no |
17 | | network | Name of the network to deploy instances to. | `string` | `"default"` | no |
18 | | port\_name | The name of the port. | `string` | `"http"` | no |
19 | | project\_id | Google Project ID | `string` | `""` | no |
20 | | region | Region for cloud resources. | `string` | `"us-central1"` | no |
21 | | roles | Permissions to be added to the created service account. | `list(any)` | `[]` | no |
22 | | scopes | List of scopes for the instance template service account | `list(any)` | `[]` | no |
23 | | service\_account | The account ID used to generate the virtual machine service account. | `string` | `""` | no |
24 | | source\_image | Source disk image. If neither source\_image nor source\_image\_family is specified, defaults to the latest public CentOS image. | `string` | `""` | no |
25 | | source\_image\_project | Project where the source image comes from. The default project contains CentOS images. | `string` | `""` | no |
26 | | startup\_script | VM startup script. | `string` | `""` | no |
27 | | subnetwork | The subnetwork to deploy to | `string` | `"default"` | no |
28 | | tags | Network tags, provided as a list | `list(string)` | `[]` | no |
29 | | target\_size | The target number of running instances for this managed instance group. This value should always be explicitly set unless this resource is attached to an autoscaler, in which case it should never be set. | `number` | `1` | no |
30 |
31 | ## Outputs
32 |
33 | | Name | Description |
34 | |------|-------------|
35 | | instance\_group | Managed instance group |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/modules/mig/main.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 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 | resource "google_service_account" "vm_sa" {
18 | project = var.project_id
19 | account_id = var.service_account
20 | }
21 |
22 | resource "google_project_iam_member" "sa_roles" {
23 | for_each = toset(var.roles)
24 |
25 | project = var.project_id
26 | role = each.key
27 | member = "serviceAccount:${google_service_account.vm_sa.email}"
28 | }
29 |
30 | module "instance_template" {
31 | source = "terraform-google-modules/vm/google//modules/instance_template"
32 | version = "~> 8.0.0"
33 |
34 | project_id = var.project_id
35 | name_prefix = var.name_prefix
36 | machine_type = var.machine_type
37 |
38 | source_image = var.source_image
39 | source_image_project = var.source_image_project
40 | disk_size_gb = var.disk_size_gb
41 | disk_type = var.disk_type
42 | auto_delete = var.disk_auto_delete
43 |
44 | startup_script = var.startup_script
45 |
46 | network = var.network
47 | subnetwork = "https://www.googleapis.com/compute/v1/projects/${var.project_id}/regions/${var.region}/subnetworks/${var.subnetwork}"
48 | service_account = {
49 | email = google_service_account.vm_sa.email
50 | scopes = var.scopes
51 | }
52 |
53 | tags = var.tags
54 | }
55 |
56 | module "mig" {
57 | source = "terraform-google-modules/vm/google//modules/mig"
58 | version = "~> 8.0.0"
59 |
60 | project_id = var.project_id
61 | mig_name = var.mig_name
62 | hostname = "${var.mig_name}-vm"
63 | region = var.region
64 |
65 | instance_template = module.instance_template.self_link
66 | target_size = var.target_size
67 |
68 | named_ports = [{
69 | name = var.port_name
70 | port = var.backend_port
71 | }]
72 |
73 | update_policy = [{
74 | type = "PROACTIVE"
75 | instance_redistribution_type = "PROACTIVE"
76 | replacement_method = "SUBSTITUTE"
77 | minimal_action = "REPLACE"
78 | max_surge_fixed = var.max_surge_fixed
79 | max_unavailable_fixed = var.max_unavailable_fixed
80 | max_unavailable_percent = null
81 | max_surge_percent = null
82 | min_ready_sec = 100
83 | }]
84 | }
85 |
--------------------------------------------------------------------------------
/modules/mig/outputs.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 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 | #!TODO
18 |
19 | output "instance_group" {
20 | description = "Managed instance group"
21 | value = module.mig.instance_group
22 | }
23 |
--------------------------------------------------------------------------------
/modules/mig/variables.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 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 | variable "project_id" {
18 | description = "Google Project ID"
19 | type = string
20 | default = ""
21 | }
22 |
23 | ## VM Service Account ##
24 | variable "service_account" {
25 | description = "The account ID used to generate the virtual machine service account."
26 | type = string
27 | default = ""
28 | }
29 |
30 | variable "roles" {
31 | description = "Permissions to be added to the created service account."
32 | type = list(any)
33 | default = []
34 | }
35 |
36 | ## VM Template ##
37 | variable "name_prefix" {
38 | description = "Name prefix for the instance template"
39 | type = string
40 | default = "vm-template-"
41 | }
42 |
43 | variable "machine_type" {
44 | description = "Machine type to create, e.g. n1-standard-1"
45 | type = string
46 | default = "n1-standard-1"
47 | }
48 | variable "source_image" {
49 | description = "Source disk image. If neither source_image nor source_image_family is specified, defaults to the latest public CentOS image."
50 | type = string
51 | default = ""
52 | }
53 |
54 | variable "source_image_project" {
55 | description = "Project where the source image comes from. The default project contains CentOS images. "
56 | type = string
57 | default = ""
58 | }
59 |
60 | variable "disk_auto_delete" {
61 | description = "Whether or not the disk should be auto-deleted."
62 | type = bool
63 | default = true
64 | }
65 |
66 | variable "disk_type" {
67 | description = "The GCE disk type. Can be either pd-ssd, local-ssd, pd-balanced or pd-standard."
68 | type = string
69 | default = "pd-standard"
70 | }
71 |
72 | variable "disk_size_gb" {
73 | description = "The size of the image in gigabytes. If not specified, it will inherit the size of its base image."
74 | type = string
75 | default = "100"
76 | }
77 |
78 | variable "scopes" {
79 | description = "List of scopes for the instance template service account"
80 | type = list(any)
81 | default = []
82 | }
83 |
84 | variable "startup_script" {
85 | description = "VM startup script."
86 | type = string
87 | default = ""
88 | }
89 |
90 | variable "tags" {
91 | description = "Network tags, provided as a list"
92 | type = list(string)
93 | default = []
94 | }
95 |
96 | ## Network ##
97 | variable "network" {
98 | description = "Name of the network to deploy instances to."
99 | type = string
100 | default = "default"
101 | }
102 |
103 | variable "subnetwork" {
104 | description = "The subnetwork to deploy to"
105 | type = string
106 | default = "default"
107 | }
108 |
109 | ## Managed Instance Group ##
110 | variable "mig_name" {
111 | description = "Name of the managed instance group."
112 | type = string
113 | default = ""
114 | }
115 |
116 | variable "region" {
117 | description = "Region for cloud resources."
118 | type = string
119 | default = "us-central1"
120 | }
121 |
122 | variable "target_size" {
123 | description = "The target number of running instances for this managed instance group. This value should always be explicitly set unless this resource is attached to an autoscaler, in which case it should never be set."
124 | type = number
125 | default = 1
126 | }
127 |
128 | variable "max_surge_fixed" {
129 | description = "The maximum number of instances that can be created above the specified targetSize during the update process."
130 | type = number
131 | }
132 |
133 | variable "max_unavailable_fixed" {
134 | description = "The maximum number of instances that can be unavailable during the update process."
135 | type = number
136 | }
137 |
138 | variable "port_name" {
139 | description = "The name of the port."
140 | type = string
141 | default = "http"
142 | }
143 |
144 | variable "backend_port" {
145 | description = "The backend port number."
146 | type = number
147 | default = 80
148 | }
149 |
--------------------------------------------------------------------------------
/modules/mig/versions.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 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 | terraform {
18 | required_version = ">= 0.13.0"
19 | required_providers {
20 | google = {
21 | source = "hashicorp/google"
22 | version = ">= 3.45"
23 | }
24 | }
25 | provider_meta "google" {
26 | module_name = "blueprints/terraform/terraform-google-waap:mig/v0.1.0"
27 | }
28 | provider_meta "google-beta" {
29 | module_name = "blueprints/terraform/terraform-google-waap:mig/v0.1.0"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/modules/waap-analytics/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## Inputs
3 |
4 | | Name | Description | Type | Default | Required |
5 | |------|-------------|------|---------|:--------:|
6 | | ca\_policy\_name | Name of Cloud Armor Security Policy resource | `string` | n/a | yes |
7 | | dataset\_name | Name of BigQuery dataset where WAAP analytics will be stored | `string` | `"waap_analytics"` | no |
8 | | log\_sink\_name | Name of BigQuery log sink | `string` | `"WAAP_log_sink"` | no |
9 | | project\_id | GCP Project ID in which analytics resources will be created | `string` | n/a | yes |
10 | | sa\_name | Name of service account with BigQuery access to be used by Looker for dashboarding | `string` | `"waap-bq-sa"` | no |
11 |
12 | ## Outputs
13 |
14 | No outputs.
15 |
16 |
17 |
--------------------------------------------------------------------------------
/modules/waap-analytics/main.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 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 | module "log_export" {
18 | source = "terraform-google-modules/log-export/google"
19 | version = "~> 7.4.2"
20 |
21 | destination_uri = module.destination.destination_uri
22 | filter = "resource.type:(http_load_balancer) AND jsonPayload.enforcedSecurityPolicy.name:(${var.ca_policy_name})"
23 | exclusions = [
24 | {
25 | name = "Ignore",
26 | description = "Ignore socket and assets",
27 | filter = "httpRequest.requestUrl =~ \"(socket.io|.js|.css)\"",
28 | disabled = false
29 | }
30 | ]
31 | log_sink_name = var.log_sink_name
32 | parent_resource_id = var.project_id
33 | parent_resource_type = "project"
34 | unique_writer_identity = true
35 | }
36 |
37 | module "destination" {
38 | source = "terraform-google-modules/log-export/google//modules/bigquery"
39 | version = "~> 7.4.2"
40 |
41 | project_id = var.project_id
42 | dataset_name = var.dataset_name
43 | log_sink_writer_identity = module.log_export.writer_identity
44 | }
45 |
46 | resource "google_service_account" "waap_analytics_sa" {
47 | project = var.project_id
48 | account_id = var.sa_name
49 | display_name = "Looker Service account for WAAP dashboarding"
50 | }
51 |
52 | resource "google_bigquery_dataset_iam_member" "editor" {
53 | project = var.project_id
54 | dataset_id = module.destination.resource_name
55 | role = "roles/bigquery.dataEditor"
56 | member = "serviceAccount:${google_service_account.waap_analytics_sa.email}"
57 | }
58 |
--------------------------------------------------------------------------------
/modules/waap-analytics/variables.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 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 | variable "project_id" {
18 | description = "GCP Project ID in which analytics resources will be created"
19 | type = string
20 | }
21 |
22 | variable "log_sink_name" {
23 | description = "Name of BigQuery log sink"
24 | type = string
25 | default = "WAAP_log_sink"
26 | }
27 |
28 | variable "ca_policy_name" {
29 | description = "Name of Cloud Armor Security Policy resource"
30 | type = string
31 | }
32 |
33 | variable "dataset_name" {
34 | description = "Name of BigQuery dataset where WAAP analytics will be stored"
35 | type = string
36 | default = "waap_analytics"
37 | }
38 |
39 | variable "sa_name" {
40 | description = "Name of service account with BigQuery access to be used by Looker for dashboarding"
41 | type = string
42 | default = "waap-bq-sa"
43 | }
44 |
--------------------------------------------------------------------------------
/modules/waap-analytics/versions.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 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 | terraform {
18 | required_version = ">= 0.13.0"
19 | required_providers {
20 | google = {
21 | source = "hashicorp/google"
22 | version = ">= 3.45"
23 | }
24 | google-beta = {
25 | source = "hashicorp/google-beta"
26 | version = ">= 3.45"
27 | }
28 | }
29 |
30 | provider_meta "google" {
31 | module_name = "blueprints/terraform/terraform-google-waap:waap-analytics/v0.1.0"
32 | }
33 | provider_meta "google-beta" {
34 | module_name = "blueprints/terraform/terraform-google-waap:waap-analytics/v0.1.0"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/test/.gitignore:
--------------------------------------------------------------------------------
1 | source.sh
2 |
--------------------------------------------------------------------------------
/test/integration/discover_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package test
16 |
17 | import (
18 | "testing"
19 |
20 | "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/tft"
21 | )
22 |
23 | func TestAll(t *testing.T) {
24 | tft.AutoDiscoverAndTest(t)
25 | }
26 |
--------------------------------------------------------------------------------
/test/integration/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/terraform-google-modules/waap/test/integration
2 |
3 | go 1.22
4 |
5 | toolchain go1.22.6
6 |
7 | require github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test v0.16.1
8 |
9 | require (
10 | cloud.google.com/go v0.110.7 // indirect
11 | cloud.google.com/go/compute v1.23.0 // indirect
12 | cloud.google.com/go/compute/metadata v0.2.3 // indirect
13 | cloud.google.com/go/iam v1.1.2 // indirect
14 | cloud.google.com/go/storage v1.33.0 // indirect
15 | github.com/agext/levenshtein v1.2.3 // indirect
16 | github.com/aws/aws-sdk-go v1.45.5 // indirect
17 | github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
18 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
19 | github.com/go-errors/errors v1.5.0 // indirect
20 | github.com/go-openapi/jsonpointer v0.20.0 // indirect
21 | github.com/go-openapi/jsonreference v0.20.2 // indirect
22 | github.com/go-openapi/swag v0.22.4 // indirect
23 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
24 | github.com/golang/protobuf v1.5.3 // indirect
25 | github.com/google/go-cmp v0.6.0 // indirect
26 | github.com/google/s2a-go v0.1.7 // indirect
27 | github.com/google/uuid v1.3.1 // indirect
28 | github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect
29 | github.com/googleapis/gax-go/v2 v2.12.0 // indirect
30 | github.com/gruntwork-io/terratest v0.47.0 // indirect
31 | github.com/hashicorp/errwrap v1.1.0 // indirect
32 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
33 | github.com/hashicorp/go-getter v1.7.5 // indirect
34 | github.com/hashicorp/go-multierror v1.1.1 // indirect
35 | github.com/hashicorp/go-safetemp v1.0.0 // indirect
36 | github.com/hashicorp/go-version v1.6.0 // indirect
37 | github.com/hashicorp/hcl/v2 v2.20.1 // indirect
38 | github.com/hashicorp/terraform-json v0.22.1 // indirect
39 | github.com/jinzhu/copier v0.4.0 // indirect
40 | github.com/jmespath/go-jmespath v0.4.0 // indirect
41 | github.com/josharian/intern v1.0.0 // indirect
42 | github.com/klauspost/compress v1.16.7 // indirect
43 | github.com/mailru/easyjson v0.7.7 // indirect
44 | github.com/mattn/go-zglob v0.0.4 // indirect
45 | github.com/mitchellh/go-homedir v1.1.0 // indirect
46 | github.com/mitchellh/go-testing-interface v1.14.2-0.20210821155943-2d9075ca8770 // indirect
47 | github.com/mitchellh/go-wordwrap v1.0.1 // indirect
48 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
49 | github.com/tidwall/gjson v1.17.1 // indirect
50 | github.com/tidwall/match v1.1.1 // indirect
51 | github.com/tidwall/pretty v1.2.1 // indirect
52 | github.com/tidwall/sjson v1.2.5 // indirect
53 | github.com/tmccombs/hcl2json v0.6.0 // indirect
54 | github.com/ulikunitz/xz v0.5.11 // indirect
55 | github.com/zclconf/go-cty v1.14.4 // indirect
56 | go.opencensus.io v0.24.0 // indirect
57 | golang.org/x/crypto v0.21.0 // indirect
58 | golang.org/x/mod v0.19.0 // indirect
59 | golang.org/x/net v0.23.0 // indirect
60 | golang.org/x/oauth2 v0.12.0 // indirect
61 | golang.org/x/sys v0.18.0 // indirect
62 | golang.org/x/text v0.14.0 // indirect
63 | golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
64 | google.golang.org/api v0.138.0 // indirect
65 | google.golang.org/appengine v1.6.8 // indirect
66 | google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect
67 | google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect
68 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
69 | google.golang.org/grpc v1.58.3 // indirect
70 | google.golang.org/protobuf v1.33.0 // indirect
71 | gopkg.in/yaml.v3 v3.0.1 // indirect
72 | k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
73 | sigs.k8s.io/kustomize/kyaml v0.17.2 // indirect
74 | )
75 |
76 | require (
77 | github.com/alexflint/go-filemutex v1.3.0 // indirect
78 | github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
79 | github.com/google/gnostic-models v0.6.8 // indirect
80 | github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f // indirect
81 | github.com/hashicorp/terraform-config-inspect v0.0.0-20240701073647-9fc3669f7553 // indirect
82 | github.com/stretchr/testify v1.9.0 // indirect
83 | golang.org/x/sync v0.4.0 // indirect
84 | golang.org/x/tools v0.13.0 // indirect
85 | sigs.k8s.io/yaml v1.4.0 // indirect
86 | )
87 |
--------------------------------------------------------------------------------
/test/setup/.gitignore:
--------------------------------------------------------------------------------
1 | terraform.tfvars
2 | source.sh
3 |
--------------------------------------------------------------------------------
/test/setup/iam.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2019 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 | int_required_roles = [
19 | "roles/artifactregistry.admin",
20 | "roles/owner",
21 | "roles/cloudkms.viewer",
22 | "roles/recaptchaenterprise.admin",
23 | ]
24 | }
25 |
26 | resource "google_service_account" "int_test" {
27 | project = module.project.project_id
28 | account_id = "ci-account"
29 | display_name = "ci-account"
30 | }
31 |
32 | resource "google_project_iam_member" "int_test" {
33 | for_each = toset(local.int_required_roles)
34 |
35 | project = module.project.project_id
36 | role = each.value
37 | member = "serviceAccount:${google_service_account.int_test.email}"
38 | }
39 |
40 | resource "google_service_account_key" "int_test" {
41 | service_account_id = google_service_account.int_test.id
42 | }
43 |
--------------------------------------------------------------------------------
/test/setup/main.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2019 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 | module "project" {
18 | source = "terraform-google-modules/project-factory/google"
19 | version = "~> 13.0"
20 |
21 | name = "ci-waap"
22 | random_project_id = "true"
23 | org_id = var.org_id
24 | folder_id = var.folder_id
25 | billing_account = var.billing_account
26 |
27 | default_service_account = "keep"
28 |
29 | activate_apis = [
30 | "apigee.googleapis.com",
31 | "artifactregistry.googleapis.com",
32 | "bigquery.googleapis.com",
33 | "cloudbuild.googleapis.com",
34 | "cloudkms.googleapis.com",
35 | "cloudresourcemanager.googleapis.com",
36 | "compute.googleapis.com",
37 | "osconfig.googleapis.com",
38 | "iap.googleapis.com",
39 | "iam.googleapis.com",
40 | "logging.googleapis.com",
41 | "monitoring.googleapis.com",
42 | "recaptchaenterprise.googleapis.com",
43 | "servicenetworking.googleapis.com",
44 | "serviceusage.googleapis.com",
45 | "dlp.googleapis.com"
46 | ]
47 | }
48 |
--------------------------------------------------------------------------------
/test/setup/outputs.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2019 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 "project_id" {
18 | value = module.project.project_id
19 | }
20 |
21 | output "sa_key" {
22 | value = google_service_account_key.int_test.private_key
23 | sensitive = true
24 | }
25 |
--------------------------------------------------------------------------------
/test/setup/variables.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2019 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 | variable "org_id" {
17 | description = "The numeric organization id"
18 | }
19 |
20 | variable "folder_id" {
21 | description = "The folder to deploy in"
22 | }
23 |
24 | variable "billing_account" {
25 | description = "The billing account id associated with the project, e.g. XXXXXX-YYYYYY-ZZZZZZ"
26 | }
27 |
--------------------------------------------------------------------------------
/test/setup/versions.tf:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2019 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 | terraform {
18 | required_version = ">= 0.13"
19 | required_providers {
20 | google = {
21 | source = "hashicorp/google"
22 | version = ">= 3.25.0"
23 | }
24 | google-beta = {
25 | source = "hashicorp/google-beta"
26 | version = ">= 3.25.0"
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------