Skip to content

Commit b95eda8

Browse files
asg release
gcp autoscaling module checkin
2 parents ef8daa0 + 7c22a53 commit b95eda8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+5898
-95
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
## 0.3.0 (January 23, 2026)
2+
FEATURES:
3+
* Official support for Cloud Connector Auto Scaling on GCP - (Requires new Marketplace Compute Image: zs-cc-ga-02042026 or greater )
4+
- add: module terraform-zscc-cloud-function-gcp for Cloud Run Function and dependency resources
5+
- update: module terraform-zscc-ccvm-gcp for autoscaling_enabled conditions including: dynamically removing stateful disk and internal_ip attributes and the addition of google_compute_autoscaler.cc_asg resource
6+
- update: module terraform-zscc-iam-service-account-gcp to include Monitoring Metric Writer role for CC SA when autoscaling is enabled
7+
- add: zsec script support for ASG greenfield and brownfield deployments
8+
9+
ENHANCEMENTS:
10+
* add: variable marketplace_image for all deployment templates defaulting to the latest available image "zs-cc-ga-02042026" upgraded to ZscalerOS 42 and supporting autoscaling
11+
* add: ssh_config creations to deployment templates outputs.tf for improvement UX
12+
* add: variable tags applied to google_compute_instance_template.cc_instance_template resource
13+
114
## 0.2.1 (February 25, 2025)
215
BUG FIXES:
316
* fix: add missing lb_vip attribute back to ilb based template userdata file generation

examples/README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,14 @@ Optional: Edit the terraform.tfvars file under your desired deployment type (ie:
4646
**Test/Greenfield Deployment Types:**
4747

4848
```
49-
Deployment Type: (base_1cc | base_1cc_zpa | base_cc_ilb | base_cc_ilb_zpa):
49+
Deployment Type: (base_1cc | base_1cc_zpa | base_cc_ilb | base_cc_ilb_zpa | base_cc_asg | base_cc_asg_zpa):
5050
base_1cc: Creates 1 new "Management" VPC with 1 CC-Mgmt subnet and 1 bastion subnet; 1 "Service" VPC with 1 CC-Service subnet and 1 workload subnet; 1 Cloud Router + NAT Gateway per VPC; 1 Ubuntu client workload with a tagged default route next-hop to Cloud Connector service network instance; 1 Bastion Host assigned a dynamic public IP; generates local key pair .pem file for ssh access to all VMs; 1 Cloud Connector compute instance template + zonal managed instance group to deploy a single Cloud Connector appliance with a dedicated service account associated for accessing Secret Manager; tagged route table pointing workload default route next-hop to the CC Instance.
5151
base_1cc_zpa: Everything from base_1cc + creates Google Cloud DNS forward zones intended for ZPA App Segment DNS redirection.
5252
base_cc_ilb: Everything from base_1cc + option to deploy multiple Cloud Connectors across multiple zonal managed instance groups behind an Internal Load Balancer (ILB) including new: backend service, forwarding rule, health check, and firewall rules needed to front all cloud connector instances for highly available/resilient workload traffic forwarding; tagged route table pointing workload default route next-hop to the ILB front end IP.
5353
base_cc_ilb_zpa: Everything from base_cc_ilb + creates Google Cloud DNS forward zones intended for ZPA App Segment DNS redirection.
54+
base_cc_asg: Everything from base_cc_ilb except the number of Cloud Connectors is determined based on min/max size variables for autoscaling group configuration. The configured Instance Group(s) will be associated with an autoscaler policy. Cloud Run Functions will also be created for VM health monitoring and resource synchronization.
55+
base_cc_asg_zpa: Everything from base_cc_asg + creates Google Cloud DNS forward zones intended for ZPA App Segment DNS redirection.
56+
5457
```
5558

5659
**2. Prod/Brownfield Deployments**
@@ -73,8 +76,9 @@ Optional: Edit the terraform.tfvars file under your desired deployment type (ie:
7376
**Prod/Brownfield Deployment Types**
7477

7578
```
76-
Deployment Type: (cc_ilb):
79+
Deployment Type: (cc_ilb | cc_asg):
7780
cc_ilb: Creates 1 new "Management" VPC with 1 CC-Mgmt subnet; 1 "Service" VPC with 1 CC-Service subnet; 1 Cloud Router + NAT Gateway per VPC; generates local key pair .pem file for ssh access to all VMs. All network infrastructure resource have conditional "byo" variables, that can be inputted if they already exist (like VPC, subnet, Cloud Router, and Cloud NAT); creates 1 Cloud Connector compute instance template with option to deploy multiple Cloud Connectors across multiple zonal managed instance groups behind an Internal Load Balancer (ILB) including new: backend service, forwarding rule, health check, and firewall rules needed to front all cloud connector instances for highly available/resilient workload traffic forwarding; and optional capability to create Google Cloud DNS forward zones intended for ZPA App Segment DNS redirection.
81+
cc_asg: All options from cc_ilb with the addition of autoscaling dependencies including autoscaler policies and Cloud Run Functions
7882
```
7983

8084
## Destroying the cluster

examples/base_1cc/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ From base_1cc directory execute:
7272
|------|------|
7373
| [google_compute_route.route_to_cc_vm](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_route) | resource |
7474
| [local_file.private_key](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource |
75+
| [local_file.ssh_config](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource |
7576
| [local_file.testbed](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource |
7677
| [local_file.user_data_file](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource |
7778
| [random_string.suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource |
@@ -109,6 +110,7 @@ From base_1cc directory execute:
109110
| <a name="input_instance_group_name"></a> [instance\_group\_name](#input\_instance\_group\_name) | The name of the Instance Group Manager. Must be 1-63 characters long and comply with RFC1035. Supported characters include lowercase letters, numbers, and hyphens | `list(string)` | <pre>[<br> ""<br>]</pre> | no |
110111
| <a name="input_instance_template_name"></a> [instance\_template\_name](#input\_instance\_template\_name) | The name of the instance template. Conflicts with variable instance\_template\_name\_prefix | `string` | `""` | no |
111112
| <a name="input_instance_template_name_prefix"></a> [instance\_template\_name\_prefix](#input\_instance\_template\_name\_prefix) | Creates a unique Instance Template name beginning with the specified prefix. Conflicts with variable instance\_template\_name | `string` | `""` | no |
113+
| <a name="input_marketplace_image"></a> [marketplace\_image](#input\_marketplace\_image) | Available marketplace image name to deploy. Zscaler recommends always deploying new instances with the latest image | `string` | `"zs-cc-ga-02042026"` | no |
112114
| <a name="input_name_prefix"></a> [name\_prefix](#input\_name\_prefix) | The name prefix for all your resources | `string` | `"zscc"` | no |
113115
| <a name="input_project"></a> [project](#input\_project) | Google Cloud project name | `string` | n/a | yes |
114116
| <a name="input_project_host"></a> [project\_host](#input\_project\_host) | Google Cloud Host Project name. Defaults to null. This variable is intended for environments where different resources might exist in separate host and service projects | `string` | `null` | no |

examples/base_1cc/main.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ resource "local_file" "user_data_file" {
151151
data "google_compute_image" "zs_cc_img" {
152152
count = var.image_name != "" ? 0 : 1
153153
project = "mpi-zscalercloudconnector-publ"
154-
name = "zs-cc-ga-02022025"
154+
name = var.marketplace_image
155155
}
156156

157157

examples/base_1cc/outputs.tf

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,52 @@ locals {
44
By default, these templates store two critical files to the "examples" directory. DO NOT delete/lose these files:
55
1. Terraform State file (terraform.tfstate) - Terraform must store state about your managed infrastructure and configuration.
66
This state is used by Terraform to map real world resources to your configuration, keep track of metadata, and to improve performance for large infrastructures.
7+
78
Terraform uses state to determine which changes to make to your infrastructure.
89
Prior to any operation, Terraform does a refresh to update the state with the real infrastructure.
10+
911
If this file is missing, you will NOT be able to make incremental changes to the environment resources without first importing state back to terraform manually.
12+
1013
2. SSH Private Key (.pem) file - Zscaler templates will attempt to create a new local private/public key pair for VM access (if a pre-existing one is not specified).
1114
You (and subsequently Zscaler) will NOT be able to remotely access these VMs once deployed without valid SSH access.
1215
***Disclaimer***
1316
17+
Login Instructions & Resource Attributes
18+
SSH to CLOUD CONNECTOR
19+
%{for k, v in local.cc_map~}
20+
ssh -F ssh_config ccvm-${k}
21+
%{endfor~}
22+
23+
All Cloud Connector Management IPs:
24+
%{for k, v in local.cc_map~}
25+
ccvm-${k} = ${v}
26+
%{endfor~}
27+
28+
All Cloud Connector Service IPs:
29+
${join("\n", module.cc_vm.cc_forwarding_ip)}
30+
31+
32+
WORKLOAD Details/Commands:
33+
SSH to WORKLOAD
34+
%{for k, v in local.workload_map~}
35+
ssh -F ssh_config workload-${k}
36+
%{endfor~}
1437
15-
### SSH to CC VM
16-
1) Copy the SSH key to the bastion host
17-
scp -i ${var.name_prefix}-key-${random_string.suffix.result}.pem ${var.name_prefix}-key-${random_string.suffix.result}.pem ubuntu@${module.bastion.public_ip}:/home/ubuntu/.
38+
WORKLOAD IPs:
39+
%{for k, v in local.workload_map~}
40+
workload-${k} = ${v}
41+
%{endfor~}
1842
19-
2) SSH to the CC VM bastion host
20-
ssh -i ${var.name_prefix}-key-${random_string.suffix.result}.pem ubuntu@${module.bastion.public_ip}
2143
22-
3) SSH to the CC
23-
ssh -i ${var.name_prefix}-key-${random_string.suffix.result}.pem zsroot@${module.cc_vm.cc_management_ip[0]} -o "proxycommand ssh -W %h:%p -i ${var.name_prefix}-key-${random_string.suffix.result}.pem ubuntu@${module.bastion.public_ip}"
44+
BASTION Jump Host Details/Commands:
45+
1) Copy the SSH key to BASTION home directory
46+
scp -F ssh_config ${var.name_prefix}-key-${random_string.suffix.result}.pem bastion:~/.
2447
25-
4) SSH to the workload host
26-
ssh -i ${var.name_prefix}-key-${random_string.suffix.result}.pem ubuntu@${module.workload.private_ip[0]} -o "proxycommand ssh -W %h:%p -i ${var.name_prefix}-key-${random_string.suffix.result}.pem ubuntu@${module.bastion.public_ip}"
48+
2) SSH to BASTION
49+
ssh -F ssh_config bastion
2750
28-
All Workload IPs. Replace private IP below with ubuntu@"ip address" in ssh example command above.
29-
${join("\n", module.workload.private_ip)}
51+
BASTION Public IP:
52+
${module.bastion.public_ip}
3053
3154
3255
Project Name:
@@ -47,17 +70,59 @@ ${join("\n", module.cc_vm.instance_group_zones)}
4770
Instance Group Names:
4871
${join("\n", module.cc_vm.instance_group_names)}
4972
50-
All Cloud Connector Instance Primary Forwarding IPs:
51-
${join("\n", module.cc_vm.cc_forwarding_ip)}
5273
5374
TB
5475
}
5576

77+
78+
locals {
79+
workload_map = {
80+
for index, ip in module.workload.private_ip :
81+
index => ip
82+
}
83+
cc_map = {
84+
for index, ip in module.cc_vm.cc_management_ip :
85+
index => ip
86+
}
87+
ssh_config_contents = <<SSH_CONFIG
88+
Host bastion
89+
HostName ${module.bastion.public_ip}
90+
User ubuntu
91+
IdentityFile ${var.name_prefix}-key-${random_string.suffix.result}.pem
92+
93+
%{for k, v in local.workload_map~}
94+
Host workload-${k}
95+
HostName ${v}
96+
User ubuntu
97+
IdentityFile ${var.name_prefix}-key-${random_string.suffix.result}.pem
98+
StrictHostKeyChecking no
99+
ProxyJump bastion
100+
ProxyCommand ssh bastion -W %h:%p
101+
%{endfor~}
102+
103+
%{for k, v in local.cc_map~}
104+
Host ccvm-${k}
105+
HostName ${v}
106+
User zsroot
107+
IdentityFile ${var.name_prefix}-key-${random_string.suffix.result}.pem
108+
StrictHostKeyChecking no
109+
ProxyJump bastion
110+
ProxyCommand ssh bastion -W %h:%p
111+
%{endfor~}
112+
SSH_CONFIG
113+
}
114+
115+
56116
output "testbedconfig" {
57117
description = "Google Cloud Testbed results"
58118
value = local.testbedconfig
59119
}
60120

121+
resource "local_file" "ssh_config" {
122+
content = local.ssh_config_contents
123+
filename = "../ssh_config"
124+
}
125+
61126
resource "local_file" "testbed" {
62127
content = local.testbedconfig
63128
filename = "../testbed.txt"

examples/base_1cc/variables.tf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,12 @@ variable "image_name" {
155155
default = ""
156156
}
157157

158+
variable "marketplace_image" {
159+
type = string
160+
description = "Available marketplace image name to deploy. Zscaler recommends always deploying new instances with the latest image"
161+
default = "zs-cc-ga-02042026"
162+
}
163+
158164
variable "support_access_enabled" {
159165
type = bool
160166
description = "Enable a specific outbound firewall rule for Cloud Connector to be able to establish connectivity for Zscaler support access. Default is true"

examples/base_1cc_zpa/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ From base_1cc_zpa directory execute:
7373
|------|------|
7474
| [google_compute_route.route_to_cc_vm](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_route) | resource |
7575
| [local_file.private_key](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource |
76+
| [local_file.ssh_config](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource |
7677
| [local_file.testbed](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource |
7778
| [local_file.user_data_file](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource |
7879
| [random_string.suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource |
@@ -111,6 +112,7 @@ From base_1cc_zpa directory execute:
111112
| <a name="input_instance_group_name"></a> [instance\_group\_name](#input\_instance\_group\_name) | The name of the Instance Group Manager. Must be 1-63 characters long and comply with RFC1035. Supported characters include lowercase letters, numbers, and hyphens | `list(string)` | <pre>[<br> ""<br>]</pre> | no |
112113
| <a name="input_instance_template_name"></a> [instance\_template\_name](#input\_instance\_template\_name) | The name of the instance template. Conflicts with variable instance\_template\_name\_prefix | `string` | `""` | no |
113114
| <a name="input_instance_template_name_prefix"></a> [instance\_template\_name\_prefix](#input\_instance\_template\_name\_prefix) | Creates a unique Instance Template name beginning with the specified prefix. Conflicts with variable instance\_template\_name | `string` | `""` | no |
115+
| <a name="input_marketplace_image"></a> [marketplace\_image](#input\_marketplace\_image) | Available marketplace image name to deploy. Zscaler recommends always deploying new instances with the latest image | `string` | `"zs-cc-ga-02042026"` | no |
114116
| <a name="input_name_prefix"></a> [name\_prefix](#input\_name\_prefix) | The name prefix for all your resources | `string` | `"zscc"` | no |
115117
| <a name="input_project"></a> [project](#input\_project) | Google Cloud project name | `string` | n/a | yes |
116118
| <a name="input_project_host"></a> [project\_host](#input\_project\_host) | Google Cloud Host Project name. Defaults to null. This variable is intended for environments where different resources might exist in separate host and service projects | `string` | `null` | no |

examples/base_1cc_zpa/main.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ resource "local_file" "user_data_file" {
150150
data "google_compute_image" "zs_cc_img" {
151151
count = var.image_name != "" ? 0 : 1
152152
project = "mpi-zscalercloudconnector-publ"
153-
name = "zs-cc-ga-02022025"
153+
name = var.marketplace_image
154154
}
155155

156156

examples/base_1cc_zpa/outputs.tf

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,52 @@ locals {
44
By default, these templates store two critical files to the "examples" directory. DO NOT delete/lose these files:
55
1. Terraform State file (terraform.tfstate) - Terraform must store state about your managed infrastructure and configuration.
66
This state is used by Terraform to map real world resources to your configuration, keep track of metadata, and to improve performance for large infrastructures.
7+
78
Terraform uses state to determine which changes to make to your infrastructure.
89
Prior to any operation, Terraform does a refresh to update the state with the real infrastructure.
10+
911
If this file is missing, you will NOT be able to make incremental changes to the environment resources without first importing state back to terraform manually.
12+
1013
2. SSH Private Key (.pem) file - Zscaler templates will attempt to create a new local private/public key pair for VM access (if a pre-existing one is not specified).
1114
You (and subsequently Zscaler) will NOT be able to remotely access these VMs once deployed without valid SSH access.
1215
***Disclaimer***
1316
17+
Login Instructions & Resource Attributes
18+
SSH to CLOUD CONNECTOR
19+
%{for k, v in local.cc_map~}
20+
ssh -F ssh_config ccvm-${k}
21+
%{endfor~}
22+
23+
All Cloud Connector Management IPs:
24+
%{for k, v in local.cc_map~}
25+
ccvm-${k} = ${v}
26+
%{endfor~}
27+
28+
All Cloud Connector Service IPs:
29+
${join("\n", module.cc_vm.cc_forwarding_ip)}
30+
31+
32+
WORKLOAD Details/Commands:
33+
SSH to WORKLOAD
34+
%{for k, v in local.workload_map~}
35+
ssh -F ssh_config workload-${k}
36+
%{endfor~}
1437
15-
### SSH to CC VM
16-
1) Copy the SSH key to the bastion host
17-
scp -i ${var.name_prefix}-key-${random_string.suffix.result}.pem ${var.name_prefix}-key-${random_string.suffix.result}.pem ubuntu@${module.bastion.public_ip}:/home/ubuntu/.
38+
WORKLOAD IPs:
39+
%{for k, v in local.workload_map~}
40+
workload-${k} = ${v}
41+
%{endfor~}
1842
19-
2) SSH to the CC VM bastion host
20-
ssh -i ${var.name_prefix}-key-${random_string.suffix.result}.pem ubuntu@${module.bastion.public_ip}
2143
22-
3) SSH to the CC
23-
ssh -i ${var.name_prefix}-key-${random_string.suffix.result}.pem zsroot@${module.cc_vm.cc_management_ip[0]} -o "proxycommand ssh -W %h:%p -i ${var.name_prefix}-key-${random_string.suffix.result}.pem ubuntu@${module.bastion.public_ip}"
44+
BASTION Jump Host Details/Commands:
45+
1) Copy the SSH key to BASTION home directory
46+
scp -F ssh_config ${var.name_prefix}-key-${random_string.suffix.result}.pem bastion:~/.
2447
25-
4) SSH to the workload host
26-
ssh -i ${var.name_prefix}-key-${random_string.suffix.result}.pem ubuntu@${module.workload.private_ip[0]} -o "proxycommand ssh -W %h:%p -i ${var.name_prefix}-key-${random_string.suffix.result}.pem ubuntu@${module.bastion.public_ip}"
48+
2) SSH to BASTION
49+
ssh -F ssh_config bastion
2750
28-
All Workload IPs. Replace private IP below with ubuntu@"ip address" in ssh example command above.
29-
${join("\n", module.workload.private_ip)}
51+
BASTION Public IP:
52+
${module.bastion.public_ip}
3053
3154
3255
Project Name:
@@ -47,17 +70,59 @@ ${join("\n", module.cc_vm.instance_group_zones)}
4770
Instance Group Names:
4871
${join("\n", module.cc_vm.instance_group_names)}
4972
50-
All Cloud Connector Instance Primary Forwarding IPs:
51-
${join("\n", module.cc_vm.cc_forwarding_ip)}
5273
5374
TB
5475
}
5576

77+
78+
locals {
79+
workload_map = {
80+
for index, ip in module.workload.private_ip :
81+
index => ip
82+
}
83+
cc_map = {
84+
for index, ip in module.cc_vm.cc_management_ip :
85+
index => ip
86+
}
87+
ssh_config_contents = <<SSH_CONFIG
88+
Host bastion
89+
HostName ${module.bastion.public_ip}
90+
User ubuntu
91+
IdentityFile ${var.name_prefix}-key-${random_string.suffix.result}.pem
92+
93+
%{for k, v in local.workload_map~}
94+
Host workload-${k}
95+
HostName ${v}
96+
User ubuntu
97+
IdentityFile ${var.name_prefix}-key-${random_string.suffix.result}.pem
98+
StrictHostKeyChecking no
99+
ProxyJump bastion
100+
ProxyCommand ssh bastion -W %h:%p
101+
%{endfor~}
102+
103+
%{for k, v in local.cc_map~}
104+
Host ccvm-${k}
105+
HostName ${v}
106+
User zsroot
107+
IdentityFile ${var.name_prefix}-key-${random_string.suffix.result}.pem
108+
StrictHostKeyChecking no
109+
ProxyJump bastion
110+
ProxyCommand ssh bastion -W %h:%p
111+
%{endfor~}
112+
SSH_CONFIG
113+
}
114+
115+
56116
output "testbedconfig" {
57117
description = "Google Cloud Testbed results"
58118
value = local.testbedconfig
59119
}
60120

121+
resource "local_file" "ssh_config" {
122+
content = local.ssh_config_contents
123+
filename = "../ssh_config"
124+
}
125+
61126
resource "local_file" "testbed" {
62127
content = local.testbedconfig
63128
filename = "../testbed.txt"

examples/base_1cc_zpa/variables.tf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,12 @@ variable "image_name" {
155155
default = ""
156156
}
157157

158+
variable "marketplace_image" {
159+
type = string
160+
description = "Available marketplace image name to deploy. Zscaler recommends always deploying new instances with the latest image"
161+
default = "zs-cc-ga-02042026"
162+
}
163+
158164
variable "domain_names" {
159165
type = map(any)
160166
description = "Domain names fqdn/wildcard to have Google Cloud DNS zone forward ZPA App Segment DNS requests to Cloud Connector"

0 commit comments

Comments
 (0)