Added new GCP terraform provider

This commit is contained in:
Will Smith 2024-04-25 20:58:25 +00:00
parent d91303ab6b
commit 19816784af
No known key found for this signature in database
GPG key ID: A4162186EBD5C0FF
48 changed files with 3788 additions and 0 deletions

70
gcp/MULTI_REGION.md Normal file
View file

@ -0,0 +1,70 @@
# GCP Multi-Server Single Region
This project will deploy Kasm Workspaces in a multi-server deployment in GCP within multiple regions of your choice. Each Kasm server role is placed in a separate subnet and you can optionally forward traffic from user sessions on the Kasm Agent through a NAT Gateway.
![Diagram][Image_Diagram]
[Image_Diagram]: https://f.hubspotusercontent30.net/hubfs/5856039/terraform/diagrams/updated/gcp-multi-region.png "Diagram"
# Pre-Configuration
Consider creating a separate GCP Project for the Kasm deployment.
### DNS Zone
There are a couple of DNS options available with this GCP Terraform. Regardless of method, Terraform will:
- Add a DNS record for the load balancer
- Add a private DNS zone and add records for the private load balancer used by Agents to communicate with the webapps
1. Create and verify the public DNS zone before deploying Terraform
- Using this method, you will create a DNS zone or use an existing DNS zone in the same GCP Project where you deploy Kasm
2. Allow Terraform to create the public DNS zone for you
- Using this method, Terraform will create a public DNS zone using the values you provide, and you must manually add the name server (NS) records to the parent DNS zone so queries are forwarded correctly
### Create Terraform service account and generate an API key
Create a GCP Service Account to use with Terraform (https://cloud.google.com/iam/docs/service-accounts-create), and generate an API key. Once the API Key credential file is downloaded, copy it's contents into the `gcp_credentials.json` file in this directory, and Terraform will use these credentials to perform all operations.
Recommended Service Account roles:
- Compute Admin
- DNS Administrator
- Network Management Admin
- Service Account Admin
### GCP APIs to enable before running Terraform
There are several GCP service APIs that must be enabled before this Terraform can build successfully. In your GCP project, navigate to each of these and ensure they are enabled before running the Terraform configuration stage below.
GCP APIs:
- Cloud DNS
- Cloud NAT
# Terraform Configuration
1. Initialize the project
terraform init
2. Open `terraform.tfvars` and update the variable values. The variable definitions, descriptions, and validation expectations can be found in the `variables.tf` file, or in the [README](./README.md).
> In order to deploy this in multiple regions, simply add all additional regions in the `kasm_deployment_regions` variable in the `terraform.tfvars` file. The first region in the list is where the Database will be deployed, thus, it is recommended to put this closest to those who will be responsible for Database administration to reduce network complexity and DB latency.
3. Verify the configuration
terraform plan
4. Deploy
terraform apply
5. Login to the Deployment as an Admin via the domain defined e.g `https://kasm.contoso.com`
> NOTE: The Load Balancer certificate can take between 15-45 min. to become active so you can access your Kasm deployment.
6. Navigate to the Agents tab, and enable each Agent after it checks in. (May take a few minutes)
# Detailed Terraform Deployment Diagram
![Detailed Diagram][Detailed_Diagram]
[Detailed_Diagram]: ./diagram/gcp_multi_region.png "Detailed Diagram"

68
gcp/MULTI_SERVER.md Normal file
View file

@ -0,0 +1,68 @@
# GCP Multi-Server Single Region
This project will deploy Kasm Workspaces in a multi-server deployment in GCP within a single region of your choice. Each Kasm server role is placed in a separate subnet and you can optionally forward traffic from user sessions on the Kasm Agent through a NAT Gateway.
![Diagram][Image_Diagram]
[Image_Diagram]: https://f.hubspotusercontent30.net/hubfs/5856039/terraform/diagrams/updated/gcp-multi-server.png "Diagram"
# Pre-Configuration
Consider creating a separate GCP Project for the Kasm deployment.
### DNS Zone
There are a couple of DNS options available with this GCP Terraform. Regardless of method, Terraform will:
- Add a DNS record for the load balancer
- Add a private DNS zone and add records for the private load balancer used by Agents to communicate with the webapps
1. Create and verify the public DNS zone before deploying Terraform
- Using this method, you will create a DNS zone or use an existing DNS zone in the same GCP Project where you deploy Kasm
2. Allow Terraform to create the public DNS zone for you
- Using this method, Terraform will create a public DNS zone using the values you provide, and you must manually add the name server (NS) records to the parent DNS zone so queries are forwarded correctly
### Create Terraform service account and generate an API key
Create a GCP Service Account to use with Terraform (https://cloud.google.com/iam/docs/service-accounts-create), and generate an API key. Once the API Key credential file is downloaded, copy it's contents into the `gcp_credentials.json` file in this directory, and Terraform will use these credentials to perform all operations.
Recommended Service Account roles:
- Compute Admin
- DNS Administrator
- Network Management Admin
- Service Account Admin
### GCP APIs to enable before running Terraform
There are several GCP service APIs that must be enabled before this Terraform can build successfully. In your GCP project, navigate to each of these and ensure they are enabled before running the Terraform configuration stage below.
GCP APIs:
- Cloud DNS
- Cloud NAT
# Terraform Configuration
1. Initialize the project
terraform init
2. Open `terraform.tfvars` and update the variable values. The variable definitions, descriptions, and validation expectations can be found in the `variables.tf` file, or in the [README](./README.md).
3. Verify the configuration
terraform plan
4. Deploy
terraform apply
5. Login to the Deployment as an Admin via the domain defined e.g `https://kasm.contoso.com`
> NOTE: The Load Balancer certificate can take between 15-45 min. to become active so you can access your Kasm deployment.
6. Navigate to the Agents tab, and enable each Agent after it checks in. (May take a few minutes)
# Detailed Terraform Deployment Diagram
![Detailed Diagram][Detailed_Diagram]
[Detailed_Diagram]: ./diagram/gcp_multi_server.png "Detailed Diagram"

279
gcp/README.md Normal file
View file

@ -0,0 +1,279 @@
# GCP Deployments
Before getting started with this Terraform, ensure the following GCP APIs or services are enabled to prevent any deployment failures or errors.
- [Cloud NAT](https://cloud.google.com/nat/docs/overview)
- [Cloud DNS API](https://console.cloud.google.com/apis/library/dns.googleapis.com)
- [IAM API](https://console.cloud.google.com/apis/library/iam.googleapis.com)
- [Cloud Resource Manager API](https://console.cloud.google.com/apis/library/cloudresourcemanager.googleapis.com)
To run this Terraform, create a GCP Service Account and generate an API key for the account. Use the below permissions as a starting point to allow the account to provision your Kasm deployment.
IAM Permissions (these are likely a little too permissive, but they are a good starting point):
- roles/compute.loadBalancerAdmin
- roles/compute.networkAdmin
- roles/compute.securityAdmin
- roles/compute.instanceAdmin
- roles/iam.serviceAccountCreator
- roles/iam.serviceAccountDeleter
- roles/iam.serviceAccountTokenCreator
- roles/iam.serviceAccountViewer
- roles/servicenetworking.networksAdmin
- roles/dns.admin
- roles/storage.admin
- roles/iam.serviceAccountUser
- roles/iam.security.admin
- roles/iam.serviceAccountKeys.create
For additional information, check out Google's IAM documentation check out these links:
- [Secure IAM](https://cloud.google.com/iam/docs/using-iam-securely)
- [Understanding Service Accounts](https://cloud.google.com/iam/docs/service-account-overview)
- [IAM Resource Hierarchy](https://cloud.google.com/iam/docs/resource-hierarchy-access-control)
- [Predefined IAM Roles](https://cloud.google.com/iam/docs/understanding-roles)
GCP offers a unique Kasm deployment experience. Due to the way they flatten their cloud network architecture, it is possible to use the same terraform deployment for both single and multi-region deployment models. Below, you will find the Terraform variable and module reference, and if you wish to see documentation specific to a Kasm deployment using this Terraform, just click one of the links below.
- [Kasm Multi-Server Deployment](./MULTI_SERVER.md)
- [Kasm Multi-Region Deployment](./MULTI_REGION.md)
<!-- END PRIVATE BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
Deploy VPC and network resources
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.0 |
| <a name="requirement_google"></a> [google](#requirement\_google) | ~> 4.0 |
| <a name="requirement_random"></a> [random](#requirement\_random) | ~> 3.0 |
| <a name="requirement_tls"></a> [tls](#requirement\_tls) | ~> 2.0 |
## Providers
No providers.
## Modules
| Name | Source | Version |
|------|--------|---------|
| <a name="module_agent_instances"></a> [agent\_instances](#module\_agent\_instances) | ./modules/compute_instance | n/a |
| <a name="module_cloud_nat"></a> [cloud\_nat](#module\_cloud\_nat) | terraform-google-modules/cloud-nat/google | ~> 4.0 |
| <a name="module_cpx_instance_group"></a> [cpx\_instance\_group](#module\_cpx\_instance\_group) | terraform-google-modules/vm/google//modules/mig | ~> 8.0 |
| <a name="module_cpx_instance_template"></a> [cpx\_instance\_template](#module\_cpx\_instance\_template) | terraform-google-modules/vm/google//modules/instance_template | ~> 8.0 |
| <a name="module_database_instance"></a> [database\_instance](#module\_database\_instance) | ./modules/compute_instance | n/a |
| <a name="module_dns_private_zone"></a> [dns\_private\_zone](#module\_dns\_private\_zone) | terraform-google-modules/cloud-dns/google | ~> 5.0 |
| <a name="module_dns_public_records"></a> [dns\_public\_records](#module\_dns\_public\_records) | ./modules/dns_records | n/a |
| <a name="module_dns_public_zone"></a> [dns\_public\_zone](#module\_dns\_public\_zone) | terraform-google-modules/cloud-dns/google | ~> 5.0 |
| <a name="module_kasm_autoscale_service_account"></a> [kasm\_autoscale\_service\_account](#module\_kasm\_autoscale\_service\_account) | ./modules/service_account_iam | n/a |
| <a name="module_passwords"></a> [passwords](#module\_passwords) | ./modules/random | n/a |
| <a name="module_public_load_balancer"></a> [public\_load\_balancer](#module\_public\_load\_balancer) | GoogleCloudPlatform/lb-http/google | ~> 9.0 |
| <a name="module_vpc"></a> [vpc](#module\_vpc) | terraform-google-modules/network/google | ~> 7.0 |
| <a name="module_webapp_instance_group"></a> [webapp\_instance\_group](#module\_webapp\_instance\_group) | terraform-google-modules/vm/google//modules/mig | ~> 8.0 |
| <a name="module_webapp_instance_template"></a> [webapp\_instance\_template](#module\_webapp\_instance\_template) | terraform-google-modules/vm/google//modules/instance_template | ~> 8.0 |
| <a name="module_webapp_private_load_balancer"></a> [webapp\_private\_load\_balancer](#module\_webapp\_private\_load\_balancer) | ./modules/private_load_balancer | n/a |
## Resources
No resources.
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_additional_agent_install_options"></a> [additional\_agent\_install\_options](#input\_additional\_agent\_install\_options) | Additional global Kasm install options. Refer to the install.sh file in the Kasm installer for additional details. | `list(string)` | `[]` | no |
| <a name="input_additional_cpx_install_options"></a> [additional\_cpx\_install\_options](#input\_additional\_cpx\_install\_options) | Additional global Kasm install options. Refer to the install.sh file in the Kasm installer for additional details. | `list(string)` | `[]` | no |
| <a name="input_additional_database_install_options"></a> [additional\_database\_install\_options](#input\_additional\_database\_install\_options) | Additional global Kasm install options. Refer to the install.sh file in the Kasm installer for additional details. | `list(string)` | `[]` | no |
| <a name="input_additional_kasm_install_options"></a> [additional\_kasm\_install\_options](#input\_additional\_kasm\_install\_options) | Additional global Kasm install options. Refer to the install.sh file in the Kasm installer for additional details. | `list(string)` | <pre>[<br> "-O"<br>]</pre> | no |
| <a name="input_additional_webapp_install_options"></a> [additional\_webapp\_install\_options](#input\_additional\_webapp\_install\_options) | Additional global Kasm install options. Refer to the install.sh file in the Kasm installer for additional details. | `list(string)` | `[]` | no |
| <a name="input_agent_gpu_enabled"></a> [agent\_gpu\_enabled](#input\_agent\_gpu\_enabled) | Whether or not to automatically install GPU libraries. NOTE: This is useless unless you deploy Kasm agents using a GPU-based instance. | `bool` | `false` | no |
| <a name="input_agent_vm_instance_config"></a> [agent\_vm\_instance\_config](#input\_agent\_vm\_instance\_config) | Agent Compute instance configuration settings | <pre>object({<br> machine_type = string<br> disk_size_gb = number<br> instance_role = string<br> name = optional(string)<br> name_prefix = optional(string)<br> disk_auto_delete = optional(bool)<br> description = optional(string)<br> disk_type = optional(string)<br> })</pre> | n/a | yes |
| <a name="input_compute_service_account"></a> [compute\_service\_account](#input\_compute\_service\_account) | Compute service account to use for CPX autoscaling | <pre>object({<br> email = optional(string)<br> scopes = list(string)<br> })</pre> | <pre>{<br> "email": "",<br> "scopes": [<br> "cloud-platform"<br> ]<br>}</pre> | no |
| <a name="input_cpx_autoscale_cool_down_period"></a> [cpx\_autoscale\_cool\_down\_period](#input\_cpx\_autoscale\_cool\_down\_period) | Time in seconds for the autoscale group to wait before evaluating the health of the webapp | `number` | `600` | no |
| <a name="input_cpx_autoscale_max_instances"></a> [cpx\_autoscale\_max\_instances](#input\_cpx\_autoscale\_max\_instances) | CPX Autoscale maximum number of instances | `number` | `5` | no |
| <a name="input_cpx_autoscale_min_instances"></a> [cpx\_autoscale\_min\_instances](#input\_cpx\_autoscale\_min\_instances) | CPX Autoscale minimum number of instances | `number` | `1` | no |
| <a name="input_cpx_autoscale_scale_in_settings"></a> [cpx\_autoscale\_scale\_in\_settings](#input\_cpx\_autoscale\_scale\_in\_settings) | CPX Autoscale scale-in settings | <pre>object({<br> fixed_replicas = number<br> time_window_sec = number<br> percent_replicas = optional(number, null)<br> })</pre> | <pre>{<br> "fixed_replicas": 1,<br> "time_window_sec": 600<br>}</pre> | no |
| <a name="input_cpx_autoscale_scale_out_cpu"></a> [cpx\_autoscale\_scale\_out\_cpu](#input\_cpx\_autoscale\_scale\_out\_cpu) | CPX Autoscale CPU percent to scale up webapps | <pre>list(object({<br> target = number<br> predictive_method = optional(string, "NONE")<br> }))</pre> | <pre>[<br> {<br> "target": 0.6<br> }<br>]</pre> | no |
| <a name="input_cpx_hostname_prefix"></a> [cpx\_hostname\_prefix](#input\_cpx\_hostname\_prefix) | CPX hostname prefix to use for instance group | `string` | `"cpx"` | no |
| <a name="input_cpx_instance_update_policy"></a> [cpx\_instance\_update\_policy](#input\_cpx\_instance\_update\_policy) | The CPX Instance group rolling update policy | <pre>list(object({<br> instance_redistribution_type = string<br> min_ready_sec = number<br> replacement_method = string<br> minimal_action = string<br> type = string<br> max_surge_fixed = optional(number, null)<br> max_surge_percent = optional(number, null) # Can only use if you run 10 or more instances<br> max_unavailable_fixed = optional(number, null)<br> max_unavailable_percent = optional(number, null) # Can only use if you run 10 or more instances<br> }))</pre> | <pre>[<br> {<br> "instance_redistribution_type": "PROACTIVE",<br> "max_surge_fixed": 3,<br> "max_unavailable_fixed": 0,<br> "min_ready_sec": 600,<br> "minimal_action": "REFRESH",<br> "replacement_method": "SUBSTITUTE",<br> "type": "PROACTIVE"<br> }<br>]</pre> | no |
| <a name="input_cpx_named_ports"></a> [cpx\_named\_ports](#input\_cpx\_named\_ports) | CPX named ports for firewall and Google service connectivity | <pre>list(object({<br> name = string<br> port = number<br> }))</pre> | <pre>[<br> {<br> "name": "https",<br> "port": 443<br> }<br>]</pre> | no |
| <a name="input_cpx_vm_instance_config"></a> [cpx\_vm\_instance\_config](#input\_cpx\_vm\_instance\_config) | CPX Compute instance configuration settings | <pre>object({<br> machine_type = string<br> disk_size_gb = string<br> disk_type = string<br> })</pre> | n/a | yes |
| <a name="input_create_kasm_autoscale_service_account"></a> [create\_kasm\_autoscale\_service\_account](#input\_create\_kasm\_autoscale\_service\_account) | Create a GCP service account capable of managing Kasm Cloud Autoscaling for GCP agents | `bool` | `false` | no |
| <a name="input_create_public_dns_zone"></a> [create\_public\_dns\_zone](#input\_create\_public\_dns\_zone) | Set to true if you wish to create a public DNS zone for this Kasm instance. If not, the public\_dns\_friendly\_name should belong to an existing DNS zone. | `bool` | `true` | no |
| <a name="input_custom_firewall_rules"></a> [custom\_firewall\_rules](#input\_custom\_firewall\_rules) | Additional, custom firewall rules | <pre>list(object({<br> name = string<br> description = optional(string, null)<br> direction = optional(string, null)<br> priority = optional(number, null)<br> ranges = optional(list(string), null)<br> source_tags = optional(list(string), null)<br> source_service_accounts = optional(list(string), null)<br> target_tags = optional(list(string), null)<br> target_service_accounts = optional(list(string), null)<br> allow = optional(list(object({<br> protocol = string<br> ports = optional(list(string))<br> })), null)<br> deny = optional(list(object({<br> protocol = string<br> ports = optional(list(string))<br> })), null)<br> log_config = optional(object({<br> metadata = string<br> }), null)<br> }))</pre> | `[]` | no |
| <a name="input_custom_kasm_routes"></a> [custom\_kasm\_routes](#input\_custom\_kasm\_routes) | Custom routes to add to VPC | <pre>list(object({<br> name = string<br> destination_range = string<br> description = optional(string, null)<br> priority = optional(number, null)<br> next_hop_internet = optional(bool, false)<br> next_hop_ip = optional(string, null)<br> next_hop_instance = optional(string, null)<br> next_hop_instance_zone = optional(string, null)<br> next_hop_vpn_tunnel = optional(string, null)<br> next_hop_ilb = optional(string, null)<br> tags = optional(list(string), [])<br> }))</pre> | `[]` | no |
| <a name="input_database_vm_instance_config"></a> [database\_vm\_instance\_config](#input\_database\_vm\_instance\_config) | Database Compute instance configuration settings | <pre>object({<br> machine_type = string<br> disk_size_gb = number<br> instance_role = string<br> name = optional(string)<br> name_prefix = optional(string)<br> disk_auto_delete = optional(bool)<br> description = optional(string)<br> disk_type = optional(string)<br> })</pre> | n/a | yes |
| <a name="input_deploy_connection_proxy"></a> [deploy\_connection\_proxy](#input\_deploy\_connection\_proxy) | Deploy Kasm Guacamole Server for RDP/SSH access to physical servers | `bool` | `false` | no |
| <a name="input_deploy_windows_hosts"></a> [deploy\_windows\_hosts](#input\_deploy\_windows\_hosts) | Create a subnet and Firewall rules for Windows hosts. These hosts must be deployed manually, or you'll need to add your own compute entry for Windows hosts. | `bool` | `false` | no |
| <a name="input_deployment_type"></a> [deployment\_type](#input\_deployment\_type) | The deployment type - Single-Server, Multi-Server, or Multi-Region | `string` | `"Multi-Server"` | no |
| <a name="input_enable_agent_nat_gateway"></a> [enable\_agent\_nat\_gateway](#input\_enable\_agent\_nat\_gateway) | Deploy Kasm Agent behind a NAT gateway | `bool` | `false` | no |
| <a name="input_google_credential_file_path"></a> [google\_credential\_file\_path](#input\_google\_credential\_file\_path) | File path to GCP account authentication file | `string` | `""` | no |
| <a name="input_kasm_admin_password"></a> [kasm\_admin\_password](#input\_kasm\_admin\_password) | The administrative user password. No special characters | `string` | `""` | no |
| <a name="input_kasm_cert_map_base_name"></a> [kasm\_cert\_map\_base\_name](#input\_kasm\_cert\_map\_base\_name) | Name to use for Kasm Global SSL certificate map | `string` | `"kasm-global-certificate-map"` | no |
| <a name="input_kasm_certificate_base_name"></a> [kasm\_certificate\_base\_name](#input\_kasm\_certificate\_base\_name) | Name to use for Kasm Global SSL certificate | `string` | `"kasm-global-tls-certificate"` | no |
| <a name="input_kasm_certificate_dns_auth_base_name"></a> [kasm\_certificate\_dns\_auth\_base\_name](#input\_kasm\_certificate\_dns\_auth\_base\_name) | Name to use for Kasm SSL DNS authorization service | `string` | `"kasm-global-certificate-dns-authorization"` | no |
| <a name="input_kasm_database_password"></a> [kasm\_database\_password](#input\_kasm\_database\_password) | The password for the database. No special characters | `string` | `""` | no |
| <a name="input_kasm_deployment_regions"></a> [kasm\_deployment\_regions](#input\_kasm\_deployment\_regions) | Kasm regions to deploy into | `list(string)` | n/a | yes |
| <a name="input_kasm_domain_name"></a> [kasm\_domain\_name](#input\_kasm\_domain\_name) | Public DNS domain name to use for Kasm deployment | `string` | n/a | yes |
| <a name="input_kasm_download_url"></a> [kasm\_download\_url](#input\_kasm\_download\_url) | Download URL for Kasm Workspaces installer | `string` | n/a | yes |
| <a name="input_kasm_firewall_security_tags"></a> [kasm\_firewall\_security\_tags](#input\_kasm\_firewall\_security\_tags) | Firewall tags to use for Kasm CPX firewall rules | <pre>object({<br> webapp = list(string)<br> database = list(string)<br> agent = list(string)<br> cpx = optional(list(string), [])<br> windows = optional(list(string), [])<br> })</pre> | <pre>{<br> "agent": [<br> "kasm-agent"<br> ],<br> "cpx": [<br> "kasm-cpx"<br> ],<br> "database": [<br> "database"<br> ],<br> "webapp": [<br> "webapp"<br> ],<br> "windows": [<br> "kasm-windows"<br> ]<br>}</pre> | no |
| <a name="input_kasm_manager_token"></a> [kasm\_manager\_token](#input\_kasm\_manager\_token) | The manager token value for Agents to authenticate to webapps. No special characters | `string` | `""` | no |
| <a name="input_kasm_project_name"></a> [kasm\_project\_name](#input\_kasm\_project\_name) | Kasm deployment project name (separate from GCP Project id or Project Name) | `string` | `""` | no |
| <a name="input_kasm_redis_password"></a> [kasm\_redis\_password](#input\_kasm\_redis\_password) | The password for the Redis server. No special characters | `string` | `""` | no |
| <a name="input_kasm_service_token"></a> [kasm\_service\_token](#input\_kasm\_service\_token) | The service registration token value for Guac RDP servers to authenticate to webapps. No special characters | `string` | `""` | no |
| <a name="input_kasm_source_image"></a> [kasm\_source\_image](#input\_kasm\_source\_image) | The source VM Image information to use for deploying Kasm. Recommended to use Ubuntu 20.04 Minimal. You can either explicitly define the source image to use, or the image project and family so that Terraform always chooses the latest. | <pre>object({<br> source_image = optional(string, null)<br> project = optional(string, null)<br> family = optional(string, null)<br> })</pre> | <pre>{<br> "family": "ubuntu-minimal-2004-lts",<br> "project": "ubuntu-os-cloud"<br>}</pre> | no |
| <a name="input_kasm_user_password"></a> [kasm\_user\_password](#input\_kasm\_user\_password) | The standard (non administrator) user password. No special characters | `string` | `""` | no |
| <a name="input_kasm_version"></a> [kasm\_version](#input\_kasm\_version) | Kasm version to deploy | `string` | `""` | no |
| <a name="input_kasm_vpc_subnet"></a> [kasm\_vpc\_subnet](#input\_kasm\_vpc\_subnet) | VPC Subnet CIDR range. All other Subnets will be automatically calculated from this seed value. | `string` | `"10.0.0.0/16"` | no |
| <a name="input_number_of_agents_per_region"></a> [number\_of\_agents\_per\_region](#input\_number\_of\_agents\_per\_region) | The number of static Kasm agents to deploy in each region. Set this to 0 to | `number` | n/a | yes |
| <a name="input_private_dns_friendly_name"></a> [private\_dns\_friendly\_name](#input\_private\_dns\_friendly\_name) | Private DNS Zone resource name | `string` | n/a | yes |
| <a name="input_project_id"></a> [project\_id](#input\_project\_id) | GCP Project ID where to deploy Kasm | `string` | n/a | yes |
| <a name="input_public_dns_friendly_name"></a> [public\_dns\_friendly\_name](#input\_public\_dns\_friendly\_name) | Public DNS Zone resource name. If not creating a new DNS Zone, make sure the desired DNS zone already exists. | `string` | n/a | yes |
| <a name="input_public_load_balancer_name"></a> [public\_load\_balancer\_name](#input\_public\_load\_balancer\_name) | GCP name for Global Public HTTPS Load balancer | `string` | `"webapp-global-load-balancer"` | no |
| <a name="input_resource_labels"></a> [resource\_labels](#input\_resource\_labels) | Default tags to add to Terraform-deployed Kasm services | `map(any)` | `null` | no |
| <a name="input_service_account_name"></a> [service\_account\_name](#input\_service\_account\_name) | Account name to use for Kasm Autoscaling service account | `string` | `""` | no |
| <a name="input_show_passwords"></a> [show\_passwords](#input\_show\_passwords) | Show Kasm passwords in root Terraform output | `bool` | `true` | no |
| <a name="input_show_sa_credentials"></a> [show\_sa\_credentials](#input\_show\_sa\_credentials) | Show GCP Service account credential file in output | `bool` | `true` | no |
| <a name="input_use_gcp_certificate_manager"></a> [use\_gcp\_certificate\_manager](#input\_use\_gcp\_certificate\_manager) | Use Certificate Manager to create and manage the Kasm public SSL certificate | `bool` | `false` | no |
| <a name="input_vpc_name"></a> [vpc\_name](#input\_vpc\_name) | Name for Kasm VPC | `string` | n/a | yes |
| <a name="input_webapp_autoscale_cool_down_period"></a> [webapp\_autoscale\_cool\_down\_period](#input\_webapp\_autoscale\_cool\_down\_period) | Time in seconds for the autoscale group to wait before evaluating the health of the webapp | `number` | `600` | no |
| <a name="input_webapp_autoscale_max_instances"></a> [webapp\_autoscale\_max\_instances](#input\_webapp\_autoscale\_max\_instances) | Webapp Autoscale maximum number of instances | `number` | `5` | no |
| <a name="input_webapp_autoscale_min_instances"></a> [webapp\_autoscale\_min\_instances](#input\_webapp\_autoscale\_min\_instances) | Webapp Autoscale minimum number of instances | `number` | `2` | no |
| <a name="input_webapp_autoscale_scale_in_settings"></a> [webapp\_autoscale\_scale\_in\_settings](#input\_webapp\_autoscale\_scale\_in\_settings) | Webapp Autoscale scale-in settings | <pre>object({<br> fixed_replicas = number<br> time_window_sec = number<br> percent_replicas = optional(number, null)<br> })</pre> | <pre>{<br> "fixed_replicas": 1,<br> "time_window_sec": 600<br>}</pre> | no |
| <a name="input_webapp_autoscale_scale_out_cpu"></a> [webapp\_autoscale\_scale\_out\_cpu](#input\_webapp\_autoscale\_scale\_out\_cpu) | Webapp Autoscale CPU percent to scale up webapps | <pre>list(object({<br> target = number<br> predictive_method = string<br> }))</pre> | <pre>[<br> {<br> "predictive_method": "NONE",<br> "target": 0.6<br> }<br>]</pre> | no |
| <a name="input_webapp_health_check"></a> [webapp\_health\_check](#input\_webapp\_health\_check) | HTTPS Managed Instance Group healthcheck for webapps. | <pre>object({<br> type = string<br> initial_delay_sec = number<br> check_interval_sec = number<br> healthy_threshold = number<br> timeout_sec = number<br> unhealthy_threshold = number<br> port = number<br> port_name = string<br> request_path = string<br> response = optional(string, "")<br> proxy_header = optional(string, "NONE")<br> request = optional(string, "")<br> host = optional(string, "")<br> enable_log = optional(bool, false)<br> enable_logging = optional(string, false)<br> })</pre> | <pre>{<br> "check_interval_sec": 30,<br> "healthy_threshold": 2,<br> "initial_delay_sec": 600,<br> "port": 443,<br> "port_name": "https",<br> "request_path": "/api/__healthcheck",<br> "timeout_sec": 10,<br> "type": "https",<br> "unhealthy_threshold": 5<br>}</pre> | no |
| <a name="input_webapp_health_check_name"></a> [webapp\_health\_check\_name](#input\_webapp\_health\_check\_name) | Name of Webapp Managed Instance Group healthcheck | `string` | `"webapp-healthcheck"` | no |
| <a name="input_webapp_hostname_prefix"></a> [webapp\_hostname\_prefix](#input\_webapp\_hostname\_prefix) | Webapp hostname prefix to use for instance group | `string` | `"webapp"` | no |
| <a name="input_webapp_instance_update_policy"></a> [webapp\_instance\_update\_policy](#input\_webapp\_instance\_update\_policy) | The Instance group rolling update policy | <pre>list(object({<br> instance_redistribution_type = string<br> min_ready_sec = number<br> replacement_method = string<br> minimal_action = string<br> type = string<br> max_surge_fixed = optional(number, null)<br> max_surge_percent = optional(number, null) # Can only use if you run 10 or more instances<br> max_unavailable_fixed = optional(number, null)<br> max_unavailable_percent = optional(number, null) # Can only use if you run 10 or more instances<br> }))</pre> | <pre>[<br> {<br> "instance_redistribution_type": "PROACTIVE",<br> "max_surge_fixed": 3,<br> "max_unavailable_fixed": 0,<br> "min_ready_sec": 600,<br> "minimal_action": "REFRESH",<br> "replacement_method": "SUBSTITUTE",<br> "type": "PROACTIVE"<br> }<br>]</pre> | no |
| <a name="input_webapp_lb_health_check"></a> [webapp\_lb\_health\_check](#input\_webapp\_lb\_health\_check) | HTTPS Load balancer and healthcheck for webapps. | <pre>object({<br> check_interval_sec = optional(number)<br> timeout_sec = optional(number)<br> healthy_threshold = optional(number)<br> unhealthy_threshold = optional(number)<br> request_path = optional(string)<br> port = optional(number)<br> host = optional(string)<br> logging = optional(bool)<br> })</pre> | <pre>{<br> "check_interval_sec": 30,<br> "healthy_threshold": 2,<br> "port": 443,<br> "request_path": "/api/__healthcheck",<br> "timeout_sec": 10,<br> "unhealthy_threshold": 3<br>}</pre> | no |
| <a name="input_webapp_named_ports"></a> [webapp\_named\_ports](#input\_webapp\_named\_ports) | Webapp named ports for firewall and Google service connectivity | <pre>list(object({<br> name = string<br> port = number<br> }))</pre> | <pre>[<br> {<br> "name": "https",<br> "port": 443<br> }<br>]</pre> | no |
| <a name="input_webapp_vm_instance_config"></a> [webapp\_vm\_instance\_config](#input\_webapp\_vm\_instance\_config) | Webapp Compute instance configuration settings | <pre>object({<br> machine_type = string<br> disk_size_gb = string<br> disk_type = string<br> })</pre> | n/a | yes |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_kasm_passwords"></a> [kasm\_passwords](#output\_kasm\_passwords) | Kasm login passwords |
| <a name="output_kasm_sa_account"></a> [kasm\_sa\_account](#output\_kasm\_sa\_account) | Kasm Service Account connection details |
<!-- END PRIVATE END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
Deploy VPC and network resources
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.0 |
| <a name="requirement_google"></a> [google](#requirement\_google) | ~> 4.0 |
| <a name="requirement_random"></a> [random](#requirement\_random) | ~> 3.0 |
| <a name="requirement_tls"></a> [tls](#requirement\_tls) | ~> 2.0 |
## Providers
No providers.
## Modules
| Name | Source | Version |
|------|--------|---------|
| <a name="module_agent_instances"></a> [agent\_instances](#module\_agent\_instances) | ./modules/compute_instance | n/a |
| <a name="module_cloud_nat"></a> [cloud\_nat](#module\_cloud\_nat) | terraform-google-modules/cloud-nat/google | ~> 4.0 |
| <a name="module_cpx_instance_group"></a> [cpx\_instance\_group](#module\_cpx\_instance\_group) | terraform-google-modules/vm/google//modules/mig | ~> 8.0 |
| <a name="module_cpx_instance_template"></a> [cpx\_instance\_template](#module\_cpx\_instance\_template) | terraform-google-modules/vm/google//modules/instance_template | ~> 8.0 |
| <a name="module_database_instance"></a> [database\_instance](#module\_database\_instance) | ./modules/compute_instance | n/a |
| <a name="module_dns_private_zone"></a> [dns\_private\_zone](#module\_dns\_private\_zone) | terraform-google-modules/cloud-dns/google | ~> 5.0 |
| <a name="module_dns_public_records"></a> [dns\_public\_records](#module\_dns\_public\_records) | ./modules/dns_records | n/a |
| <a name="module_dns_public_zone"></a> [dns\_public\_zone](#module\_dns\_public\_zone) | terraform-google-modules/cloud-dns/google | ~> 5.0 |
| <a name="module_kasm_autoscale_service_account"></a> [kasm\_autoscale\_service\_account](#module\_kasm\_autoscale\_service\_account) | ./modules/service_account_iam | n/a |
| <a name="module_passwords"></a> [passwords](#module\_passwords) | ./modules/random | n/a |
| <a name="module_public_load_balancer"></a> [public\_load\_balancer](#module\_public\_load\_balancer) | GoogleCloudPlatform/lb-http/google | ~> 9.0 |
| <a name="module_vpc"></a> [vpc](#module\_vpc) | terraform-google-modules/network/google | ~> 7.0 |
| <a name="module_webapp_instance_group"></a> [webapp\_instance\_group](#module\_webapp\_instance\_group) | terraform-google-modules/vm/google//modules/mig | ~> 8.0 |
| <a name="module_webapp_instance_template"></a> [webapp\_instance\_template](#module\_webapp\_instance\_template) | terraform-google-modules/vm/google//modules/instance_template | ~> 8.0 |
| <a name="module_webapp_private_load_balancer"></a> [webapp\_private\_load\_balancer](#module\_webapp\_private\_load\_balancer) | ./modules/private_load_balancer | n/a |
## Resources
No resources.
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_additional_agent_install_options"></a> [additional\_agent\_install\_options](#input\_additional\_agent\_install\_options) | Additional global Kasm install options. Refer to the install.sh file in the Kasm installer for additional details. | `list(string)` | `[]` | no |
| <a name="input_additional_cpx_install_options"></a> [additional\_cpx\_install\_options](#input\_additional\_cpx\_install\_options) | Additional global Kasm install options. Refer to the install.sh file in the Kasm installer for additional details. | `list(string)` | `[]` | no |
| <a name="input_additional_database_install_options"></a> [additional\_database\_install\_options](#input\_additional\_database\_install\_options) | Additional global Kasm install options. Refer to the install.sh file in the Kasm installer for additional details. | `list(string)` | `[]` | no |
| <a name="input_additional_kasm_install_options"></a> [additional\_kasm\_install\_options](#input\_additional\_kasm\_install\_options) | Additional global Kasm install options. Refer to the install.sh file in the Kasm installer for additional details. | `list(string)` | <pre>[<br> "-O"<br>]</pre> | no |
| <a name="input_additional_webapp_install_options"></a> [additional\_webapp\_install\_options](#input\_additional\_webapp\_install\_options) | Additional global Kasm install options. Refer to the install.sh file in the Kasm installer for additional details. | `list(string)` | `[]` | no |
| <a name="input_agent_gpu_enabled"></a> [agent\_gpu\_enabled](#input\_agent\_gpu\_enabled) | Whether or not to automatically install GPU libraries. NOTE: This is useless unless you deploy Kasm agents using a GPU-based instance. | `bool` | `false` | no |
| <a name="input_agent_vm_instance_config"></a> [agent\_vm\_instance\_config](#input\_agent\_vm\_instance\_config) | Agent Compute instance configuration settings | <pre>object({<br> machine_type = string<br> disk_size_gb = number<br> instance_role = string<br> name = optional(string)<br> name_prefix = optional(string)<br> disk_auto_delete = optional(bool)<br> description = optional(string)<br> disk_type = optional(string)<br> })</pre> | n/a | yes |
| <a name="input_compute_service_account"></a> [compute\_service\_account](#input\_compute\_service\_account) | Compute service account to use for CPX autoscaling | <pre>object({<br> email = optional(string)<br> scopes = list(string)<br> })</pre> | <pre>{<br> "email": "",<br> "scopes": [<br> "cloud-platform"<br> ]<br>}</pre> | no |
| <a name="input_cpx_autoscale_cool_down_period"></a> [cpx\_autoscale\_cool\_down\_period](#input\_cpx\_autoscale\_cool\_down\_period) | Time in seconds for the autoscale group to wait before evaluating the health of the webapp | `number` | `600` | no |
| <a name="input_cpx_autoscale_max_instances"></a> [cpx\_autoscale\_max\_instances](#input\_cpx\_autoscale\_max\_instances) | CPX Autoscale maximum number of instances | `number` | `5` | no |
| <a name="input_cpx_autoscale_min_instances"></a> [cpx\_autoscale\_min\_instances](#input\_cpx\_autoscale\_min\_instances) | CPX Autoscale minimum number of instances | `number` | `1` | no |
| <a name="input_cpx_autoscale_scale_in_settings"></a> [cpx\_autoscale\_scale\_in\_settings](#input\_cpx\_autoscale\_scale\_in\_settings) | CPX Autoscale scale-in settings | <pre>object({<br> fixed_replicas = number<br> time_window_sec = number<br> percent_replicas = optional(number, null)<br> })</pre> | <pre>{<br> "fixed_replicas": 1,<br> "time_window_sec": 600<br>}</pre> | no |
| <a name="input_cpx_autoscale_scale_out_cpu"></a> [cpx\_autoscale\_scale\_out\_cpu](#input\_cpx\_autoscale\_scale\_out\_cpu) | CPX Autoscale CPU percent to scale up webapps | <pre>list(object({<br> target = number<br> predictive_method = optional(string, "NONE")<br> }))</pre> | <pre>[<br> {<br> "target": 0.6<br> }<br>]</pre> | no |
| <a name="input_cpx_hostname_prefix"></a> [cpx\_hostname\_prefix](#input\_cpx\_hostname\_prefix) | CPX hostname prefix to use for instance group | `string` | `"cpx"` | no |
| <a name="input_cpx_instance_update_policy"></a> [cpx\_instance\_update\_policy](#input\_cpx\_instance\_update\_policy) | The CPX Instance group rolling update policy | <pre>list(object({<br> instance_redistribution_type = string<br> min_ready_sec = number<br> replacement_method = string<br> minimal_action = string<br> type = string<br> max_surge_fixed = optional(number, null)<br> max_surge_percent = optional(number, null) # Can only use if you run 10 or more instances<br> max_unavailable_fixed = optional(number, null)<br> max_unavailable_percent = optional(number, null) # Can only use if you run 10 or more instances<br> }))</pre> | <pre>[<br> {<br> "instance_redistribution_type": "PROACTIVE",<br> "max_surge_fixed": 3,<br> "max_unavailable_fixed": 0,<br> "min_ready_sec": 600,<br> "minimal_action": "REFRESH",<br> "replacement_method": "SUBSTITUTE",<br> "type": "PROACTIVE"<br> }<br>]</pre> | no |
| <a name="input_cpx_named_ports"></a> [cpx\_named\_ports](#input\_cpx\_named\_ports) | CPX named ports for firewall and Google service connectivity | <pre>list(object({<br> name = string<br> port = number<br> }))</pre> | <pre>[<br> {<br> "name": "https",<br> "port": 443<br> }<br>]</pre> | no |
| <a name="input_cpx_vm_instance_config"></a> [cpx\_vm\_instance\_config](#input\_cpx\_vm\_instance\_config) | CPX Compute instance configuration settings | <pre>object({<br> machine_type = string<br> disk_size_gb = string<br> disk_type = string<br> })</pre> | n/a | yes |
| <a name="input_create_kasm_autoscale_service_account"></a> [create\_kasm\_autoscale\_service\_account](#input\_create\_kasm\_autoscale\_service\_account) | Create a GCP service account capable of managing Kasm Cloud Autoscaling for GCP agents | `bool` | `false` | no |
| <a name="input_create_public_dns_zone"></a> [create\_public\_dns\_zone](#input\_create\_public\_dns\_zone) | Set to true if you wish to create a public DNS zone for this Kasm instance. If not, the public\_dns\_friendly\_name should belong to an existing DNS zone. | `bool` | `true` | no |
| <a name="input_custom_firewall_rules"></a> [custom\_firewall\_rules](#input\_custom\_firewall\_rules) | Additional, custom firewall rules | <pre>list(object({<br> name = string<br> description = optional(string, null)<br> direction = optional(string, null)<br> priority = optional(number, null)<br> ranges = optional(list(string), null)<br> source_tags = optional(list(string), null)<br> source_service_accounts = optional(list(string), null)<br> target_tags = optional(list(string), null)<br> target_service_accounts = optional(list(string), null)<br> allow = optional(list(object({<br> protocol = string<br> ports = optional(list(string))<br> })), null)<br> deny = optional(list(object({<br> protocol = string<br> ports = optional(list(string))<br> })), null)<br> log_config = optional(object({<br> metadata = string<br> }), null)<br> }))</pre> | `[]` | no |
| <a name="input_custom_kasm_routes"></a> [custom\_kasm\_routes](#input\_custom\_kasm\_routes) | Custom routes to add to VPC | <pre>list(object({<br> name = string<br> destination_range = string<br> description = optional(string, null)<br> priority = optional(number, null)<br> next_hop_internet = optional(bool, false)<br> next_hop_ip = optional(string, null)<br> next_hop_instance = optional(string, null)<br> next_hop_instance_zone = optional(string, null)<br> next_hop_vpn_tunnel = optional(string, null)<br> next_hop_ilb = optional(string, null)<br> tags = optional(list(string), [])<br> }))</pre> | `[]` | no |
| <a name="input_database_vm_instance_config"></a> [database\_vm\_instance\_config](#input\_database\_vm\_instance\_config) | Database Compute instance configuration settings | <pre>object({<br> machine_type = string<br> disk_size_gb = number<br> instance_role = string<br> name = optional(string)<br> name_prefix = optional(string)<br> disk_auto_delete = optional(bool)<br> description = optional(string)<br> disk_type = optional(string)<br> })</pre> | n/a | yes |
| <a name="input_deploy_connection_proxy"></a> [deploy\_connection\_proxy](#input\_deploy\_connection\_proxy) | Deploy Kasm Guacamole Server for RDP/SSH access to physical servers | `bool` | `false` | no |
| <a name="input_deploy_windows_hosts"></a> [deploy\_windows\_hosts](#input\_deploy\_windows\_hosts) | Create a subnet and Firewall rules for Windows hosts. These hosts must be deployed manually, or you'll need to add your own compute entry for Windows hosts. | `bool` | `false` | no |
| <a name="input_deployment_type"></a> [deployment\_type](#input\_deployment\_type) | The deployment type - Single-Server, Multi-Server, or Multi-Region | `string` | `"Multi-Server"` | no |
| <a name="input_enable_agent_nat_gateway"></a> [enable\_agent\_nat\_gateway](#input\_enable\_agent\_nat\_gateway) | Deploy Kasm Agent behind a NAT gateway | `bool` | `false` | no |
| <a name="input_google_credential_file_path"></a> [google\_credential\_file\_path](#input\_google\_credential\_file\_path) | File path to GCP account authentication file | `string` | `""` | no |
| <a name="input_kasm_admin_password"></a> [kasm\_admin\_password](#input\_kasm\_admin\_password) | The administrative user password. No special characters | `string` | `""` | no |
| <a name="input_kasm_cert_map_base_name"></a> [kasm\_cert\_map\_base\_name](#input\_kasm\_cert\_map\_base\_name) | Name to use for Kasm Global SSL certificate map | `string` | `"kasm-global-certificate-map"` | no |
| <a name="input_kasm_certificate_base_name"></a> [kasm\_certificate\_base\_name](#input\_kasm\_certificate\_base\_name) | Name to use for Kasm Global SSL certificate | `string` | `"kasm-global-tls-certificate"` | no |
| <a name="input_kasm_certificate_dns_auth_base_name"></a> [kasm\_certificate\_dns\_auth\_base\_name](#input\_kasm\_certificate\_dns\_auth\_base\_name) | Name to use for Kasm SSL DNS authorization service | `string` | `"kasm-global-certificate-dns-authorization"` | no |
| <a name="input_kasm_database_password"></a> [kasm\_database\_password](#input\_kasm\_database\_password) | The password for the database. No special characters | `string` | `""` | no |
| <a name="input_kasm_deployment_regions"></a> [kasm\_deployment\_regions](#input\_kasm\_deployment\_regions) | Kasm regions to deploy into | `list(string)` | n/a | yes |
| <a name="input_kasm_domain_name"></a> [kasm\_domain\_name](#input\_kasm\_domain\_name) | Public DNS domain name to use for Kasm deployment | `string` | n/a | yes |
| <a name="input_kasm_download_url"></a> [kasm\_download\_url](#input\_kasm\_download\_url) | Download URL for Kasm Workspaces installer | `string` | n/a | yes |
| <a name="input_kasm_firewall_security_tags"></a> [kasm\_firewall\_security\_tags](#input\_kasm\_firewall\_security\_tags) | Firewall tags to use for Kasm CPX firewall rules | <pre>object({<br> webapp = list(string)<br> database = list(string)<br> agent = list(string)<br> cpx = optional(list(string), [])<br> windows = optional(list(string), [])<br> })</pre> | <pre>{<br> "agent": [<br> "kasm-agent"<br> ],<br> "cpx": [<br> "kasm-cpx"<br> ],<br> "database": [<br> "database"<br> ],<br> "webapp": [<br> "webapp"<br> ],<br> "windows": [<br> "kasm-windows"<br> ]<br>}</pre> | no |
| <a name="input_kasm_manager_token"></a> [kasm\_manager\_token](#input\_kasm\_manager\_token) | The manager token value for Agents to authenticate to webapps. No special characters | `string` | `""` | no |
| <a name="input_kasm_project_name"></a> [kasm\_project\_name](#input\_kasm\_project\_name) | Kasm deployment project name (separate from GCP Project id or Project Name) | `string` | `""` | no |
| <a name="input_kasm_redis_password"></a> [kasm\_redis\_password](#input\_kasm\_redis\_password) | The password for the Redis server. No special characters | `string` | `""` | no |
| <a name="input_kasm_service_token"></a> [kasm\_service\_token](#input\_kasm\_service\_token) | The service registration token value for Guac RDP servers to authenticate to webapps. No special characters | `string` | `""` | no |
| <a name="input_kasm_source_image"></a> [kasm\_source\_image](#input\_kasm\_source\_image) | The source VM Image information to use for deploying Kasm. Recommended to use Ubuntu 20.04 Minimal. You can either explicitly define the source image to use, or the image project and family so that Terraform always chooses the latest. | <pre>object({<br> source_image = optional(string, null)<br> project = optional(string, null)<br> family = optional(string, null)<br> })</pre> | <pre>{<br> "family": "ubuntu-minimal-2004-lts",<br> "project": "ubuntu-os-cloud"<br>}</pre> | no |
| <a name="input_kasm_user_password"></a> [kasm\_user\_password](#input\_kasm\_user\_password) | The standard (non administrator) user password. No special characters | `string` | `""` | no |
| <a name="input_kasm_version"></a> [kasm\_version](#input\_kasm\_version) | Kasm version to deploy | `string` | `""` | no |
| <a name="input_kasm_vpc_subnet"></a> [kasm\_vpc\_subnet](#input\_kasm\_vpc\_subnet) | VPC Subnet CIDR range. All other Subnets will be automatically calculated from this seed value. | `string` | `"10.0.0.0/16"` | no |
| <a name="input_number_of_agents_per_region"></a> [number\_of\_agents\_per\_region](#input\_number\_of\_agents\_per\_region) | The number of static Kasm agents to deploy in each region. Set this to 0 to | `number` | n/a | yes |
| <a name="input_private_dns_friendly_name"></a> [private\_dns\_friendly\_name](#input\_private\_dns\_friendly\_name) | Private DNS Zone resource name | `string` | n/a | yes |
| <a name="input_project_id"></a> [project\_id](#input\_project\_id) | GCP Project ID where to deploy Kasm | `string` | n/a | yes |
| <a name="input_public_dns_friendly_name"></a> [public\_dns\_friendly\_name](#input\_public\_dns\_friendly\_name) | Public DNS Zone resource name. If not creating a new DNS Zone, make sure the desired DNS zone already exists. | `string` | n/a | yes |
| <a name="input_public_load_balancer_name"></a> [public\_load\_balancer\_name](#input\_public\_load\_balancer\_name) | GCP name for Global Public HTTPS Load balancer | `string` | `"webapp-global-load-balancer"` | no |
| <a name="input_resource_labels"></a> [resource\_labels](#input\_resource\_labels) | Default tags to add to Terraform-deployed Kasm services | `map(any)` | `null` | no |
| <a name="input_service_account_name"></a> [service\_account\_name](#input\_service\_account\_name) | Account name to use for Kasm Autoscaling service account | `string` | `""` | no |
| <a name="input_show_passwords"></a> [show\_passwords](#input\_show\_passwords) | Show Kasm passwords in root Terraform output | `bool` | `true` | no |
| <a name="input_show_sa_credentials"></a> [show\_sa\_credentials](#input\_show\_sa\_credentials) | Show GCP Service account credential file in output | `bool` | `true` | no |
| <a name="input_use_gcp_certificate_manager"></a> [use\_gcp\_certificate\_manager](#input\_use\_gcp\_certificate\_manager) | Use Certificate Manager to create and manage the Kasm public SSL certificate | `bool` | `false` | no |
| <a name="input_vpc_name"></a> [vpc\_name](#input\_vpc\_name) | Name for Kasm VPC | `string` | n/a | yes |
| <a name="input_webapp_autoscale_cool_down_period"></a> [webapp\_autoscale\_cool\_down\_period](#input\_webapp\_autoscale\_cool\_down\_period) | Time in seconds for the autoscale group to wait before evaluating the health of the webapp | `number` | `600` | no |
| <a name="input_webapp_autoscale_max_instances"></a> [webapp\_autoscale\_max\_instances](#input\_webapp\_autoscale\_max\_instances) | Webapp Autoscale maximum number of instances | `number` | `5` | no |
| <a name="input_webapp_autoscale_min_instances"></a> [webapp\_autoscale\_min\_instances](#input\_webapp\_autoscale\_min\_instances) | Webapp Autoscale minimum number of instances | `number` | `2` | no |
| <a name="input_webapp_autoscale_scale_in_settings"></a> [webapp\_autoscale\_scale\_in\_settings](#input\_webapp\_autoscale\_scale\_in\_settings) | Webapp Autoscale scale-in settings | <pre>object({<br> fixed_replicas = number<br> time_window_sec = number<br> percent_replicas = optional(number, null)<br> })</pre> | <pre>{<br> "fixed_replicas": 1,<br> "time_window_sec": 600<br>}</pre> | no |
| <a name="input_webapp_autoscale_scale_out_cpu"></a> [webapp\_autoscale\_scale\_out\_cpu](#input\_webapp\_autoscale\_scale\_out\_cpu) | Webapp Autoscale CPU percent to scale up webapps | <pre>list(object({<br> target = number<br> predictive_method = string<br> }))</pre> | <pre>[<br> {<br> "predictive_method": "NONE",<br> "target": 0.6<br> }<br>]</pre> | no |
| <a name="input_webapp_health_check"></a> [webapp\_health\_check](#input\_webapp\_health\_check) | HTTPS Managed Instance Group healthcheck for webapps. | <pre>object({<br> type = string<br> initial_delay_sec = number<br> check_interval_sec = number<br> healthy_threshold = number<br> timeout_sec = number<br> unhealthy_threshold = number<br> port = number<br> port_name = string<br> request_path = string<br> response = optional(string, "")<br> proxy_header = optional(string, "NONE")<br> request = optional(string, "")<br> host = optional(string, "")<br> enable_log = optional(bool, false)<br> enable_logging = optional(string, false)<br> })</pre> | <pre>{<br> "check_interval_sec": 30,<br> "healthy_threshold": 2,<br> "initial_delay_sec": 600,<br> "port": 443,<br> "port_name": "https",<br> "request_path": "/api/__healthcheck",<br> "timeout_sec": 10,<br> "type": "https",<br> "unhealthy_threshold": 5<br>}</pre> | no |
| <a name="input_webapp_health_check_name"></a> [webapp\_health\_check\_name](#input\_webapp\_health\_check\_name) | Name of Webapp Managed Instance Group healthcheck | `string` | `"webapp-healthcheck"` | no |
| <a name="input_webapp_hostname_prefix"></a> [webapp\_hostname\_prefix](#input\_webapp\_hostname\_prefix) | Webapp hostname prefix to use for instance group | `string` | `"webapp"` | no |
| <a name="input_webapp_instance_update_policy"></a> [webapp\_instance\_update\_policy](#input\_webapp\_instance\_update\_policy) | The Instance group rolling update policy | <pre>list(object({<br> instance_redistribution_type = string<br> min_ready_sec = number<br> replacement_method = string<br> minimal_action = string<br> type = string<br> max_surge_fixed = optional(number, null)<br> max_surge_percent = optional(number, null) # Can only use if you run 10 or more instances<br> max_unavailable_fixed = optional(number, null)<br> max_unavailable_percent = optional(number, null) # Can only use if you run 10 or more instances<br> }))</pre> | <pre>[<br> {<br> "instance_redistribution_type": "PROACTIVE",<br> "max_surge_fixed": 3,<br> "max_unavailable_fixed": 0,<br> "min_ready_sec": 600,<br> "minimal_action": "REFRESH",<br> "replacement_method": "SUBSTITUTE",<br> "type": "PROACTIVE"<br> }<br>]</pre> | no |
| <a name="input_webapp_lb_health_check"></a> [webapp\_lb\_health\_check](#input\_webapp\_lb\_health\_check) | HTTPS Load balancer and healthcheck for webapps. | <pre>object({<br> check_interval_sec = optional(number)<br> timeout_sec = optional(number)<br> healthy_threshold = optional(number)<br> unhealthy_threshold = optional(number)<br> request_path = optional(string)<br> port = optional(number)<br> host = optional(string)<br> logging = optional(bool)<br> })</pre> | <pre>{<br> "check_interval_sec": 30,<br> "healthy_threshold": 2,<br> "port": 443,<br> "request_path": "/api/__healthcheck",<br> "timeout_sec": 10,<br> "unhealthy_threshold": 3<br>}</pre> | no |
| <a name="input_webapp_named_ports"></a> [webapp\_named\_ports](#input\_webapp\_named\_ports) | Webapp named ports for firewall and Google service connectivity | <pre>list(object({<br> name = string<br> port = number<br> }))</pre> | <pre>[<br> {<br> "name": "https",<br> "port": 443<br> }<br>]</pre> | no |
| <a name="input_webapp_vm_instance_config"></a> [webapp\_vm\_instance\_config](#input\_webapp\_vm\_instance\_config) | Webapp Compute instance configuration settings | <pre>object({<br> machine_type = string<br> disk_size_gb = string<br> disk_type = string<br> })</pre> | n/a | yes |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_kasm_passwords"></a> [kasm\_passwords](#output\_kasm\_passwords) | Kasm login passwords |
| <a name="output_kasm_sa_account"></a> [kasm\_sa\_account](#output\_kasm\_sa\_account) | Kasm Service Account connection details |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

281
gcp/locals.tf Normal file
View file

@ -0,0 +1,281 @@
locals {
## GCP Credential file - set to null for users who wish to use environment/cached credentials.
## NOTE: If you wish to use direct-to-agent, then this credential file must be populated since
## Let's Encrypt will use it to automatically add the Certificate's DNS validation record.
gcp_credentials = var.google_credential_file_path == "" ? null : file(var.google_credential_file_path)
## Kasm Passwords
admin_password = var.kasm_admin_password == "" ? module.passwords[0].password : var.kasm_admin_password
user_password = var.kasm_user_password == "" ? module.passwords[1].password : var.kasm_user_password
database_password = var.kasm_database_password == "" ? module.passwords[2].password : var.kasm_database_password
redis_password = var.kasm_redis_password == "" ? module.passwords[3].password : var.kasm_redis_password
service_token = var.kasm_service_token == "" ? module.passwords[4].password : var.kasm_service_token
manager_token = var.kasm_manager_token == "" ? module.passwords[5].password : var.kasm_manager_token
## List of Kasm resources to install
kasm_resources = compact(["webapp", "proxy", "database", "agent", var.deploy_connection_proxy ? "cpx" : "", var.deploy_windows_hosts ? "windows" : ""])
## Generate VPC subnet map for Kasm resources
cidr_mask_size = (21 - split("/", var.kasm_vpc_subnet)[1])
vpc_subnets = [for idx in range(pow(2, local.cidr_mask_size)) : cidrsubnet(var.kasm_vpc_subnet, local.cidr_mask_size, idx)]
vpc_subnets_by_region = { for idx, region in var.kasm_deployment_regions : region => local.vpc_subnets[idx] }
kasm_subnets = flatten([for r_index, region in var.kasm_deployment_regions :
[for idx, subnet in local.kasm_resources : {
subnet_name = "${region}-${subnet}-subnet"
subnet_ip = cidrsubnet(local.vpc_subnets_by_region[region], 3, idx)
subnet_region = region
subnet_private_access = subnet == "proxy" ? false : true
purpose = subnet == "proxy" ? "REGIONAL_MANAGED_PROXY" : "PRIVATE"
role = subnet == "proxy" ? "ACTIVE" : null
} if !(r_index != 0 && (subnet == "database"))]
])
## Create VPC routes. Use the custom_kasm_routes variable to add your own custom
## route entries to the VPC's route table.
kasm_routes = concat(flatten([var.custom_kasm_routes]))
## VPC outputs
vpc_network_self_link = module.vpc.network_self_link
## Webapp Userdata Sartup configurations
webapp_startup_scripts = { for region in var.kasm_deployment_regions : region => templatefile("${path.module}/userdata/webapp_bootstrap.sh", {
DB_PRIVATE_IP = local.database_private_ip
KASM_DB_PASS = local.database_password
KASM_REDIS_PASS = local.redis_password
KASM_DOWNLOAD_URL = var.kasm_download_url
ADDITIONAL_WEBAPP_INSTALL_ARGS = join(" ", distinct(flatten([var.additional_kasm_install_options, var.additional_webapp_install_options])))
KASM_ZONE_NAME = region
}) }
## Webapp instance output
webapp_instance_template_self_link = { for region in var.kasm_deployment_regions : region => module.webapp_instance_template[region].self_link }
## Database Userdata Startup configuration
database_startup_config = [templatefile("${path.module}/userdata/database_bootstrap.sh", {
KASM_USER_PASS = local.user_password
KASM_ADMIN_PASS = local.admin_password
KASM_MANAGER_TOKEN = local.manager_token
KASM_SERVICE_TOKEN = local.service_token
KASM_DB_PASS = local.database_password
KASM_REDIS_PASS = local.redis_password
KASM_DOWNLOAD_URL = var.kasm_download_url
ADDITIONAL_DATABASE_INSTALL_ARGS = join(" ", distinct(flatten([var.additional_kasm_install_options, var.additional_database_install_options])))
})]
## Database outputs
database_private_ip = module.database_instance.private_ip[0]
## Agent Userdata Startup configurations
agent_public_ip = var.enable_agent_nat_gateway ? [] : [{ network_tier = "PREMIUM" }]
agent_startup_configs = { for region in var.kasm_deployment_regions : region => [
for idx in range(var.number_of_agents_per_region) : templatefile("${path.module}/userdata/agent_bootstrap.sh", {
KASM_MANAGER_TOKEN = local.manager_token
PRIVATE_LB_HOSTNAME = "${region}-lb.private.${var.kasm_domain_name}"
KASM_DOWNLOAD_URL = var.kasm_download_url
ADDITIONAL_AGENT_INSTALL_ARGS = join(" ", distinct(flatten([var.additional_kasm_install_options, var.additional_agent_install_options])))
GPU_ENABLED = var.agent_gpu_enabled ? "1" : "0"
})]
}
## CPX Userdata Sartup configurations
cpx_startup_configs = { for region in var.kasm_deployment_regions : region => templatefile("${path.module}/userdata/cpx_bootstrap.sh", {
PRIVATE_LB_HOSTNAME = "${region}-lb.private.${var.kasm_domain_name}"
KASM_SERVICE_TOKEN = local.service_token
KASM_DOWNLOAD_URL = var.kasm_download_url
ADDITIONAL_CPX_INSTALL_ARGS = join(" ", distinct(flatten([var.additional_kasm_install_options, var.additional_cpx_install_options])))
KASM_ZONE_NAME = region
}) }
## CPX Outputs
cpx_instance_template_self_link = var.deploy_connection_proxy ? { for region in var.kasm_deployment_regions : region => module.cpx_instance_template[region].self_link } : {}
## Public load balancer backends
public_load_balancer_backends = {
kasm-global-lb-backend = {
description = "Global HTTPS Load balancer backend"
protocol = "HTTPS"
port = var.webapp_named_ports[0].port
port_name = var.webapp_named_ports[0].name
timeout_sec = 1800
enable_cdn = false
health_check = var.webapp_lb_health_check
custom_request_headers = []
custom_response_headers = []
compression_mode = ""
affinity_cookie_ttl_sec = null
connection_draining_timeout_sec = null
edge_security_policy = null
security_policy = null
session_affinity = null
log_config = {
enable = false
sample_rate = null
}
iap_config = {
enable = false
oauth2_client_id = null
oauth2_client_secret = null
}
groups = [for region in var.kasm_deployment_regions :
{
group = module.webapp_instance_group[region].instance_group
max_utilization = 0.8
description = "${region} Webapp Public LB Backend"
balancing_mode = "UTILIZATION"
max_connections = null
max_connections_per_endpoint = null
max_connections_per_instance = null
max_rate = null
max_rate_per_endpoint = null
max_rate_per_instance = null
capacity_scaler = null
}
]
}
}
private_load_balancer_backends = { for region in var.kasm_deployment_regions : region => [
{
group = module.webapp_instance_group[region].instance_group
max_utilization = 0.8
description = "${title(region)} Webapp private load balancer backend"
balancing_mode = "UTILIZATION"
failover = false
capacity_scaler = 1
}]
}
## TLS Certificate locals for public Load Balancer
managed_ssl_domains = var.use_gcp_certificate_manager ? [] : [var.kasm_domain_name]
use_lb_ssl = var.use_gcp_certificate_manager ? false : true
cert_manager_map_name = null #var.use_gcp_certificate_manager ? "" : "" #module.certificate_manager[0].certificate_map_id : ""
public_dns_records = [{
name = ""
type = "A"
ttl = 300
records = [
module.public_load_balancer.external_ip
]
}]
## Private DNS Load balancer records
private_dns_records = [for region in var.kasm_deployment_regions : {
name = "${region}-lb"
type = "A"
ttl = 300
records = [module.webapp_private_load_balancer[region].ip_address]
}]
## VPC Firewall rules
firewall_rules = concat(flatten([[for rule in [
{
name = "kasm-webapp"
description = "Webapp HTTPS ingress"
priority = 1000
direction = "INGRESS"
ranges = ["130.211.0.0/22", "35.191.0.0/16"]
source_tags = compact(flatten([
var.kasm_firewall_security_tags.agent,
var.deploy_connection_proxy ? var.kasm_firewall_security_tags.cpx : []
]))
target_tags = var.kasm_firewall_security_tags.webapp
allow = [{
protocol = "tcp"
ports = ["443"]
}]
},
{
name = "kasm-agent"
description = "Kasm Agent ingress"
priority = 1010
direction = "INGRESS"
source_tags = var.kasm_firewall_security_tags.webapp
target_tags = var.kasm_firewall_security_tags.agent
allow = [{
protocol = "tcp"
ports = ["443"]
}]
},
{
name = "kasm-agent-to-private-lb"
description = "Private Load Balancer ingress from agent to webapp"
priority = 1020
direction = "INGRESS"
target_tags = var.kasm_firewall_security_tags.webapp
ranges = [for dict in local.kasm_subnets : dict.subnet_ip if contains(split("-", dict.subnet_name), "proxy")]
allow = [{
protocol = "tcp"
ports = ["443"]
}]
},
{
name = "kasm-database"
description = "Kasm Database ingress"
priority = 1030
direction = "INGRESS"
source_tags = var.kasm_firewall_security_tags.webapp
target_tags = var.kasm_firewall_security_tags.database
ranges = ["35.235.240.0/20"]
allow = [{
protocol = "tcp"
ports = ["5432", "6379"]
}]
},
{
name = "ssh-access"
description = "Allow GCP web-based SSH access to Kasm services"
prioritiy = 1040
direction = "INGRESS"
ranges = ["35.235.240.0/20"]
target_tags = compact(flatten([
var.kasm_firewall_security_tags.webapp,
var.kasm_firewall_security_tags.agent,
var.kasm_firewall_security_tags.database,
var.deploy_connection_proxy ? var.kasm_firewall_security_tags.cpx : []
]))
allow = [{
protocol = "tcp"
ports = ["22"]
}]
},
var.deploy_connection_proxy ? {
name = "connection-proxy"
description = "Kasm Connection Proxy ingress"
priority = 1050
direction = "INGRESS"
source_tags = var.kasm_firewall_security_tags.webapp
target_tags = var.kasm_firewall_security_tags.cpx
ranges = ["35.235.240.0/20"]
allow = [{
protocol = "tcp"
ports = ["443"]
}]
} : null,
var.deploy_windows_hosts ? {
name = "windows-hosts"
description = "Kasm Windows-managed Host ingress"
priority = 1060
direction = "INGRESS"
source_tags = flatten([
var.kasm_firewall_security_tags.cpx,
var.kasm_firewall_security_tags.webapp
])
target_tags = var.kasm_firewall_security_tags.windows
allow = [{
protocol = "tcp"
ports = ["3389", "4902"]
}]
} : null
] : rule if rule != null], var.custom_firewall_rules]))
## Labels to apply to resources
resource_labels = merge(var.resource_labels, {
deployed_by = "terraform"
deployed_application = "kasm_workspaces"
customer = "weave"
kasm_version = try(replace("v${var.kasm_version}", ".", "-"), null)
project_name = try(lower(var.kasm_project_name), null)
deployment_type = try(lower(var.deployment_type), null)
})
}

288
gcp/main.tf Normal file
View file

@ -0,0 +1,288 @@
/*
* Deploy VPC and network resources
*/
## VPC
module "vpc" {
source = "terraform-google-modules/network/google"
version = "~> 9.0"
network_name = var.vpc_name
project_id = var.project_id
routing_mode = "GLOBAL"
subnets = local.kasm_subnets
routes = local.kasm_routes
firewall_rules = local.firewall_rules
mtu = "1500"
}
## NAT Gateway
module "cloud_nat" {
source = "terraform-google-modules/cloud-nat/google"
version = "~> 5.0"
for_each = toset(var.kasm_deployment_regions)
name = "${each.key}-nat-gateway"
network = module.vpc.network_self_link
project_id = var.project_id
region = each.key
create_router = true
router = "${each.key}-router"
enable_dynamic_port_allocation = true
enable_endpoint_independent_mapping = false
}
/*
* Deploy Kasm WebApps
*/
## Create Kasm Webapp Instance Groups
module "webapp_instance_template" {
source = "terraform-google-modules/vm/google//modules/instance_template"
version = "~> 11.0"
for_each = toset(var.kasm_deployment_regions)
project_id = var.project_id
network = local.vpc_network_self_link
subnetwork = "${each.key}-webapp-subnet"
region = each.key
service_account = var.compute_service_account
name_prefix = substr("${each.key}-${var.webapp_hostname_prefix}-template", 0, 37)
source_image_project = var.kasm_source_image.project
source_image_family = var.kasm_source_image.family
machine_type = var.webapp_vm_instance_config.machine_type
disk_size_gb = var.webapp_vm_instance_config.disk_size_gb
disk_type = var.webapp_vm_instance_config.disk_type
tags = var.kasm_firewall_security_tags.webapp
startup_script = local.webapp_startup_scripts[each.key]
labels = local.resource_labels
}
## Create Kasm Webapp Instance groups
module "webapp_instance_group" {
source = "terraform-google-modules/vm/google//modules/mig"
version = "~> 11.0"
for_each = toset(var.kasm_deployment_regions)
project_id = var.project_id
mig_name = "${each.key}-${var.webapp_hostname_prefix}-group"
instance_template = local.webapp_instance_template_self_link[each.key]
region = each.key
hostname = "${each.key}-${var.webapp_hostname_prefix}"
named_ports = var.webapp_named_ports
health_check_name = "${each.key}-${var.webapp_health_check_name}"
health_check = var.webapp_health_check
autoscaling_enabled = true
max_replicas = var.webapp_autoscale_max_instances
min_replicas = var.webapp_autoscale_min_instances
cooldown_period = var.webapp_autoscale_cool_down_period
autoscaling_cpu = var.webapp_autoscale_scale_out_cpu
autoscaling_scale_in_control = var.webapp_autoscale_scale_in_settings
}
/*
* Deploy WebApp public and private load balancers
*/
## Internal (private) Load balancers for Agent auth
module "webapp_private_load_balancer" {
source = "./modules/private_load_balancer"
for_each = toset(var.kasm_deployment_regions)
project = var.project_id
region = each.key
network = module.vpc.network_name
subnetwork = one([for subnet in module.vpc.subnets_names : subnet if can(regex("${each.key}.webapp", subnet)) ])
name = "${each.key}-webapp-private-load-balancer"
port_range = var.webapp_named_ports[0].port
ip_protocol = "TCP"
named_port = var.webapp_named_ports[0].name
health_check = var.webapp_health_check
backends = local.private_load_balancer_backends[each.key]
depends_on = [ module.vpc ]
}
## Public access Load balancer for client Kasm access
module "public_load_balancer" {
source = "GoogleCloudPlatform/lb-http/google"
version = "~> 10.0"
name = var.public_load_balancer_name
project = var.project_id
https_redirect = true
ssl = local.use_lb_ssl
managed_ssl_certificate_domains = local.managed_ssl_domains
certificate_map = local.cert_manager_map_name
firewall_networks = []
firewall_projects = []
backends = local.public_load_balancer_backends
}
/*
* Deploy Kasm Database
*/
## Database Instance
module "database_instance" {
source = "./modules/compute_instance"
instance_details = var.database_vm_instance_config
instance_network = local.vpc_network_self_link
instance_subnetwork = "${var.kasm_deployment_regions[0]}-database-subnet"
source_image = [var.kasm_source_image]
service_account = [var.compute_service_account]
cloud_init_script = local.database_startup_config
security_tags = var.kasm_firewall_security_tags.database
resource_labels = local.resource_labels
public_access_config = []
kasm_region = var.kasm_deployment_regions[0]
depends_on = [ module.vpc ]
}
/*
* Deploy Kasm Agents
*/
## Agent Instances
module "agent_instances" {
source = "./modules/compute_instance"
for_each = toset(var.kasm_deployment_regions)
number_of_instances = var.number_of_agents_per_region
instance_details = var.agent_vm_instance_config
instance_network = local.vpc_network_self_link
instance_subnetwork = "${each.key}-agent-subnet"
source_image = [var.kasm_source_image]
service_account = [var.compute_service_account]
cloud_init_script = local.agent_startup_configs[each.key]
security_tags = var.kasm_firewall_security_tags.agent
resource_labels = local.resource_labels
public_access_config = local.agent_public_ip
kasm_region = each.key
depends_on = [ module.vpc ]
}
/*
* Deploy Kasm Connection Proxy (CPX/Guac)
*/
## Create Kasm CPX Instance templates
module "cpx_instance_template" {
source = "terraform-google-modules/vm/google//modules/instance_template"
version = "~> 11.0"
for_each = var.deploy_connection_proxy ? toset(var.kasm_deployment_regions) : []
project_id = var.project_id
network = local.vpc_network_self_link
subnetwork = "${each.key}-cpx-subnet"
region = each.key
service_account = var.compute_service_account
name_prefix = substr("${each.key}-${var.cpx_hostname_prefix}-template", 0, 63)
source_image_project = var.kasm_source_image.project
source_image_family = var.kasm_source_image.family
machine_type = var.cpx_vm_instance_config.machine_type
disk_size_gb = var.cpx_vm_instance_config.disk_size_gb
disk_type = var.cpx_vm_instance_config.disk_type
tags = var.kasm_firewall_security_tags.webapp
startup_script = local.cpx_startup_configs[each.key]
labels = local.resource_labels
depends_on = [ module.vpc ]
}
## Create Kasm Webapp Instance groups
module "cpx_instance_group" {
source = "terraform-google-modules/vm/google//modules/mig"
version = "~> 11.0"
for_each = var.deploy_connection_proxy ? toset(var.kasm_deployment_regions) : []
project_id = var.project_id
mig_name = "${each.key}-cpx-instance-group"
instance_template = local.cpx_instance_template_self_link[each.key]
region = each.key
hostname = "${each.key}-${var.cpx_hostname_prefix}-group"
named_ports = var.cpx_named_ports
autoscaling_enabled = true
max_replicas = var.cpx_autoscale_max_instances
min_replicas = var.cpx_autoscale_min_instances
cooldown_period = var.cpx_autoscale_cool_down_period
autoscaling_cpu = var.cpx_autoscale_scale_out_cpu
autoscaling_scale_in_control = var.cpx_autoscale_scale_in_settings
}
/*
* Deploy DNS
*/
## Public DNS Zone
module "dns_public_zone" {
source = "terraform-google-modules/cloud-dns/google"
version = "~> 5.0"
count = var.create_public_dns_zone ? 1 : 0
type = "public"
project_id = var.project_id
name = var.public_dns_friendly_name
domain = "${var.kasm_domain_name}."
recordsets = local.public_dns_records
}
## Add records to existing public DNS zone (e.g. this TF didn't create the public zone)
module "dns_public_records" {
source = "./modules/dns_records"
count = var.create_public_dns_zone ? 0 : 1
project_id = var.project_id
name = var.public_dns_friendly_name
domain = var.kasm_domain_name
recordsets = local.public_dns_records
}
## Private DNS Zone
module "dns_private_zone" {
source = "terraform-google-modules/cloud-dns/google"
version = "~> 5.0"
type = "private"
project_id = var.project_id
name = var.private_dns_friendly_name
domain = "private.${var.kasm_domain_name}."
private_visibility_config_networks = [local.vpc_network_self_link]
recordsets = local.private_dns_records
}
/*
* Create Kasm Autoscale service IAM account
*/
module "kasm_autoscale_service_account" {
source = "./modules/service_account_iam"
count = var.create_kasm_autoscale_service_account ? 1 : 0
project_id = var.project_id
account_id = var.service_account_name
}
/*
* Create passwords (always run, but not alwayse used). Refer
* to locals.tf for conditional usage.
*/
module "passwords" {
source = "./modules/random"
count = 6
kasm_version = var.kasm_version
}
/*
* Create Public GCP-managed SSL cert in Cert Manager
*
* Using Certificate manager only works with this Terraform if you deploy it independently,
* then reference the created certificate map ID directly. There is a concurrency issue
* with Terraform that causes problems, so, this and the referenced module serve as an
* example deployment method until this issue is resolved.
*/
# module "certificate_manager" {
# source = "./modules/certificate_manager"
# count = var.use_gcp_certificate_manager ? 1 : 0
# domain_name = var.kasm_domain_name
# dns_managed_zone_name = local.public_dns_name
# certificate_name = local.tls_certificate_name
# certificate_dns_authorization_name = local.tls_certificate_dns_auth_name
# certificate_map_name = local.tls_certificate_map_name
# resource_labels = var.resource_labels
# }

View file

@ -0,0 +1,50 @@
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.0 |
| <a name="requirement_google"></a> [google](#requirement\_google) | ~> 4.0 |
## Providers
| Name | Version |
|------|---------|
| <a name="provider_google"></a> [google](#provider\_google) | 4.81.0 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [google_certificate_manager_certificate.cert](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/certificate_manager_certificate) | resource |
| [google_certificate_manager_certificate_map.cert_map](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/certificate_manager_certificate_map) | resource |
| [google_certificate_manager_certificate_map_entry.cert_map_entry](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/certificate_manager_certificate_map_entry) | resource |
| [google_certificate_manager_dns_authorization.cert_auth](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/certificate_manager_dns_authorization) | resource |
| [google_dns_record_set.cert_dns_record](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_certificate_description"></a> [certificate\_description](#input\_certificate\_description) | Certificate description | `string` | `"Global Load Balancer SSL Certificate"` | no |
| <a name="input_certificate_dns_authorization_description"></a> [certificate\_dns\_authorization\_description](#input\_certificate\_dns\_authorization\_description) | Description of the DNS Authorization job in Certificate Manager | `string` | `"Global Load Balancer certificate DNS authorization"` | no |
| <a name="input_certificate_dns_authorization_name"></a> [certificate\_dns\_authorization\_name](#input\_certificate\_dns\_authorization\_name) | The name of the DNS Authorization job in Certificate Manager | `string` | n/a | yes |
| <a name="input_certificate_map_description"></a> [certificate\_map\_description](#input\_certificate\_map\_description) | Description of the certificate map | `string` | `"Global HTTPS Load Balancer Certificate Map"` | no |
| <a name="input_certificate_map_name"></a> [certificate\_map\_name](#input\_certificate\_map\_name) | Certificate map name | `string` | n/a | yes |
| <a name="input_certificate_name"></a> [certificate\_name](#input\_certificate\_name) | Certificate name in Certificate manager. Must be globally unique. | `string` | n/a | yes |
| <a name="input_certificate_scope"></a> [certificate\_scope](#input\_certificate\_scope) | GCP Certificate scope | `string` | `"DEFAULT"` | no |
| <a name="input_create_wildcard"></a> [create\_wildcard](#input\_create\_wildcard) | Add wildcard domain to certificate | `bool` | `true` | no |
| <a name="input_dns_managed_zone_name"></a> [dns\_managed\_zone\_name](#input\_dns\_managed\_zone\_name) | The name of the GCP DNS zone | `string` | n/a | yes |
| <a name="input_domain_name"></a> [domain\_name](#input\_domain\_name) | Kasm deployment domain name | `string` | n/a | yes |
| <a name="input_resource_labels"></a> [resource\_labels](#input\_resource\_labels) | Labels to add to all created resources in this project | `map(any)` | `{}` | no |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_certificate_map_id"></a> [certificate\_map\_id](#output\_certificate\_map\_id) | The value of the generated certificate map for use with the external load balancer |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

View file

@ -0,0 +1,47 @@
resource "google_certificate_manager_dns_authorization" "cert_auth" {
name = var.certificate_dns_authorization_name
description = var.certificate_dns_authorization_description
domain = var.domain_name
labels = var.resource_labels
}
resource "google_dns_record_set" "cert_dns_record" {
name = google_certificate_manager_dns_authorization.cert_auth.dns_resource_record[0].name
type = "CNAME"
ttl = 30
managed_zone = var.dns_managed_zone_name
rrdatas = [google_certificate_manager_dns_authorization.cert_auth.dns_resource_record[0].data]
}
resource "google_certificate_manager_certificate" "cert" {
name = var.certificate_name
description = var.certificate_description
scope = var.certificate_scope
labels = var.resource_labels
managed {
domains = compact([
google_certificate_manager_dns_authorization.cert_auth.domain,
var.create_wildcard ? "*.${google_certificate_manager_dns_authorization.cert_auth.domain}" : ""
])
dns_authorizations = [
google_certificate_manager_dns_authorization.cert_auth.id
]
}
depends_on = [google_dns_record_set.cert_dns_record]
}
resource "google_certificate_manager_certificate_map" "cert_map" {
name = var.certificate_map_name
description = var.certificate_map_description
labels = var.resource_labels
}
resource "google_certificate_manager_certificate_map_entry" "cert_map_entry" {
name = "${var.certificate_map_name}-entry"
map = google_certificate_manager_certificate_map.cert_map.name
certificates = [google_certificate_manager_certificate.cert.id]
matcher = "PRIMARY"
labels = var.resource_labels
}

View file

@ -0,0 +1,4 @@
output "certificate_map_id" {
description = "The value of the generated certificate map for use with the external load balancer"
value = google_certificate_manager_certificate_map.cert_map.id
}

View file

@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}

View file

@ -0,0 +1,81 @@
variable "domain_name" {
description = "Kasm deployment domain name"
type = string
}
variable "dns_managed_zone_name" {
description = "The name of the GCP DNS zone"
type = string
}
variable "certificate_name" {
description = "Certificate name in Certificate manager. Must be globally unique."
type = string
validation {
condition = can(regex("^[a-z0-9-]{5,63}", var.certificate_name))
error_message = "The certificate_name must be globally unique, and can only be between 5-63 characters consisting of only lower case letters, number, and dash (-)."
}
}
variable "certificate_dns_authorization_name" {
description = "The name of the DNS Authorization job in Certificate Manager"
type = string
validation {
condition = can(regex("^[a-z0-9-]{5,63}", var.certificate_dns_authorization_name))
error_message = "The certificate_dns_authorization_name must be globally unique, and can only be between 5-63 characters consisting of only lower case letters, number, and dash (-)."
}
}
variable "certificate_map_name" {
description = "Certificate map name"
type = string
validation {
condition = can(regex("^[a-z0-9-]{5,63}", var.certificate_map_name))
error_message = "The certificate_map_name must be globally unique, and can only be between 5-63 characters consisting of only lower case letters, number, and dash (-)."
}
}
## Pre-set values
variable "certificate_description" {
description = "Certificate description"
type = string
default = "Global Load Balancer SSL Certificate"
}
variable "certificate_dns_authorization_description" {
description = "Description of the DNS Authorization job in Certificate Manager"
type = string
default = "Global Load Balancer certificate DNS authorization"
}
variable "certificate_map_description" {
description = "Description of the certificate map"
type = string
default = "Global HTTPS Load Balancer Certificate Map"
}
variable "certificate_scope" {
description = "GCP Certificate scope"
type = string
default = "DEFAULT"
validation {
condition = contains(["DEFAULT", "EDGE_CACHE"], var.certificate_scope)
error_message = "The certificate_scope variable can only be one of: DEFAULT or EDGE_CACHE."
}
}
variable "create_wildcard" {
description = "Add wildcard domain to certificate"
type = bool
default = true
}
variable "resource_labels" {
description = "Labels to add to all created resources in this project"
type = map(any)
default = {}
}

View file

@ -0,0 +1,56 @@
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.0 |
| <a name="requirement_google"></a> [google](#requirement\_google) | ~> 4.0 |
## Providers
| Name | Version |
|------|---------|
| <a name="provider_google"></a> [google](#provider\_google) | 4.70.0 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [google_compute_instance.kasm_instance](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance) | resource |
| [google_compute_image.kasm_image](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/compute_image) | data source |
| [google_compute_zones.available](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/compute_zones) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_allow_stopping_for_update"></a> [allow\_stopping\_for\_update](#input\_allow\_stopping\_for\_update) | Allow GCP service accounts to stop the instance and initiate an updates | `bool` | `false` | no |
| <a name="input_cloud_init_script"></a> [cloud\_init\_script](#input\_cloud\_init\_script) | Startup script used to provision the instance | `list(string)` | n/a | yes |
| <a name="input_enable_instance_vtpm"></a> [enable\_instance\_vtpm](#input\_enable\_instance\_vtpm) | Enable the Trusted Platform Module to secure the instance | `bool` | `true` | no |
| <a name="input_enable_integrity_monitoring"></a> [enable\_integrity\_monitoring](#input\_enable\_integrity\_monitoring) | Enable instance integrity monitoring | `bool` | `true` | no |
| <a name="input_enable_secure_boot"></a> [enable\_secure\_boot](#input\_enable\_secure\_boot) | Enable instance secure boot | `bool` | `null` | no |
| <a name="input_instance_delete_protection"></a> [instance\_delete\_protection](#input\_instance\_delete\_protection) | Prevent instance from accidental deletion | `bool` | `true` | no |
| <a name="input_instance_details"></a> [instance\_details](#input\_instance\_details) | Instance details to configure for Kasm instance | <pre>object({<br> machine_type = string<br> disk_size_gb = number<br> instance_role = string<br> name = optional(string, "")<br> name_prefix = optional(string, "")<br> disk_auto_delete = optional(bool, true)<br> description = optional(string, null)<br> disk_type = optional(string, "pd-balanced")<br> })</pre> | n/a | yes |
| <a name="input_instance_network"></a> [instance\_network](#input\_instance\_network) | VPC Network self\_link where instance is to be deployed. Usually inferred by the subnetwork, however, if subnetwork names overlap, this can create confusion. This value is optional, and required if subnetwork names are reused across VPCs. | `string` | `null` | no |
| <a name="input_instance_nic_stack_type"></a> [instance\_nic\_stack\_type](#input\_instance\_nic\_stack\_type) | The default network interface type | `string` | `"IPV4_ONLY"` | no |
| <a name="input_instance_nic_type"></a> [instance\_nic\_type](#input\_instance\_nic\_type) | The default network interface type | `string` | `"GVNIC"` | no |
| <a name="input_instance_subnetwork"></a> [instance\_subnetwork](#input\_instance\_subnetwork) | Name of subnetwork where to deploy the instance | `string` | n/a | yes |
| <a name="input_kasm_region"></a> [kasm\_region](#input\_kasm\_region) | GCP region in which to deploy this Kasm instance | `string` | n/a | yes |
| <a name="input_number_of_instances"></a> [number\_of\_instances](#input\_number\_of\_instances) | Number of instances to deploy in this region | `number` | `1` | no |
| <a name="input_public_access_config"></a> [public\_access\_config](#input\_public\_access\_config) | Enable public IP access for instance | <pre>list(object({<br> nat_ip = optional(string)<br> public_ptr_domain_name = optional(string)<br> network_tier = optional(string)<br> }))</pre> | n/a | yes |
| <a name="input_resource_labels"></a> [resource\_labels](#input\_resource\_labels) | Labels to apply to the instance | `map(any)` | `null` | no |
| <a name="input_security_tags"></a> [security\_tags](#input\_security\_tags) | Security tags to use with firewall rules to allow instance access | `list(string)` | n/a | yes |
| <a name="input_service_account"></a> [service\_account](#input\_service\_account) | Service account to use for instance auto updates | <pre>list(object({<br> email = optional(string)<br> scopes = list(string)<br> }))</pre> | `[]` | no |
| <a name="input_source_image"></a> [source\_image](#input\_source\_image) | The source VM Image information to use for deploying Kasm. Recommended to use Ubuntu 20.04 Minimal. You can either explicitly define the source image to use, or the image project and family so that Terraform always chooses the latest. | <pre>list(object({<br> source_image = optional(string, null)<br> project = optional(string, null)<br> family = optional(string, null)<br> }))</pre> | n/a | yes |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_private_ip"></a> [private\_ip](#output\_private\_ip) | Instance private IP address. |
| <a name="output_public_ip"></a> [public\_ip](#output\_public\_ip) | Instance public IP address (if applicable) |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

View file

@ -0,0 +1,68 @@
data "google_compute_image" "kasm_image" {
project = var.source_image[0].project
family = var.source_image[0].family
name = var.source_image[0].source_image
}
data "google_compute_zones" "available" {
region = var.kasm_region
}
resource "google_compute_instance" "kasm_instance" {
count = var.number_of_instances
name = var.instance_details.name == "" ? substr("${var.kasm_region}-${var.instance_details.name_prefix}-0${count.index + 1}", 0, 63) : var.instance_details.name
description = var.instance_details.description
machine_type = var.instance_details.machine_type
deletion_protection = var.instance_details.instance_role == "agent" ? false : var.instance_delete_protection
tags = var.security_tags
labels = var.resource_labels
allow_stopping_for_update = var.allow_stopping_for_update
metadata_startup_script = var.cloud_init_script[count.index]
zone = data.google_compute_zones.available.names[count.index]
boot_disk {
auto_delete = var.instance_details.disk_auto_delete
initialize_params {
size = var.instance_details.disk_size_gb
type = var.instance_details.disk_type
image = data.google_compute_image.kasm_image.self_link
labels = var.resource_labels
}
}
network_interface {
network = var.instance_network
subnetwork = var.instance_subnetwork
nic_type = var.instance_nic_type
stack_type = var.instance_nic_stack_type
dynamic "access_config" {
for_each = var.public_access_config
content {
nat_ip = lookup(access_config.value, "nat_ip", null)
public_ptr_domain_name = lookup(access_config.value, "public_ptr_domain_name", null)
network_tier = lookup(access_config.value, "network_tier", null)
}
}
}
shielded_instance_config {
enable_secure_boot = var.enable_secure_boot
enable_vtpm = var.enable_instance_vtpm
enable_integrity_monitoring = var.enable_integrity_monitoring
}
dynamic "service_account" {
for_each = var.service_account
content {
email = lookup(service_account.value, "email", null)
scopes = lookup(service_account.value, "scopes", null)
}
}
lifecycle {
ignore_changes = [
metadata["ssh-keys"]
]
}
}

View file

@ -0,0 +1,12 @@
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
## Deploy Ksam Database
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
output "private_ip" {
description = "Instance private IP address."
value = google_compute_instance.kasm_instance[*].network_interface[0].network_ip
}
output "public_ip" {
description = "Instance public IP address (if applicable)"
value = google_compute_instance.kasm_instance[*].network_interface[0].access_config
}

View file

@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}

View file

@ -0,0 +1,152 @@
variable "instance_details" {
description = "Instance details to configure for Kasm instance"
type = object({
machine_type = string
disk_size_gb = number
instance_role = string
name = optional(string, "")
name_prefix = optional(string, "")
disk_auto_delete = optional(bool, true)
description = optional(string, null)
disk_type = optional(string, "pd-balanced")
})
validation {
condition = contains(["database", "webapp", "agent", "cpx"], var.instance_details.instance_role)
error_message = "The instance_details name attribute can only be a max of 63 characters consisting of lower case letters, numbers, and dash (-)."
}
validation {
condition = var.instance_details.name == "" ? true : can(regex("^[a-z0-9-]{4,63}", var.instance_details.name))
error_message = "The instance_details name attribute can only be a max of 63 characters consisting of lower case letters, numbers, and dash (-)."
}
validation {
condition = var.instance_details.name_prefix == "" ? true : can(regex("^[a-z0-9-]{4,63}", var.instance_details.name_prefix))
error_message = "The instance_details name_prefix attribute can only be a max of 63 characters consisting of lower case letters, numbers, and dash (-)."
}
validation {
condition = var.instance_details.disk_size_gb > 30
error_message = "The instance_details disk_size_gb should be larger than 30GB to support Kasm, logging, and other required services. Recommended minimum size is 50GB."
}
validation {
condition = contains(["pd-standard", "pd-balanced", "pd-ssd", "pd-extreme"], var.instance_details.disk_type)
error_message = "The disk_type can only be one of pd-standard, pd-balanced, pd-extreme, or pd-ssd. Refer to https://cloud.google.com/compute/docs/disks for more details."
}
}
variable "kasm_region" {
description = "GCP region in which to deploy this Kasm instance"
type = string
}
variable "security_tags" {
description = "Security tags to use with firewall rules to allow instance access"
type = list(string)
}
variable "cloud_init_script" {
description = "Startup script used to provision the instance"
type = list(string)
}
variable "instance_subnetwork" {
description = "Name of subnetwork where to deploy the instance"
type = string
}
variable "source_image" {
description = "The source VM Image information to use for deploying Kasm. Recommended to use Ubuntu 20.04 Minimal. You can either explicitly define the source image to use, or the image project and family so that Terraform always chooses the latest."
type = list(object({
source_image = optional(string, null)
project = optional(string, null)
family = optional(string, null)
}))
}
## Pre-set values
variable "number_of_instances" {
description = "Number of instances to deploy in this region"
type = number
default = 1
}
variable "instance_network" {
description = "VPC Network self_link where instance is to be deployed. Usually inferred by the subnetwork, however, if subnetwork names overlap, this can create confusion. This value is optional, and required if subnetwork names are reused across VPCs."
type = string
default = null
}
variable "allow_stopping_for_update" {
description = "Allow GCP service accounts to stop the instance and initiate an updates"
type = bool
default = false
}
variable "instance_delete_protection" {
description = "Prevent instance from accidental deletion"
type = bool
default = true
}
variable "instance_nic_type" {
description = "The default network interface type"
type = string
default = "GVNIC"
validation {
condition = contains(["GVNIC", "VIRTIO_NET"], var.instance_nic_type)
error_message = "The instance_nic_type can only be one of GVNIC or VIRTIO_NET. GVNIC currently offers enhanced performance and capabilities. Refer to this document (https://cloud.google.com/compute/docs/networking/using-gvnic) for more information."
}
}
variable "instance_nic_stack_type" {
description = "The default network interface type"
type = string
default = "IPV4_ONLY"
validation {
condition = contains(["IPV4_ONLY", "IPV4_IPV6"], var.instance_nic_stack_type)
error_message = "The instance_nic_stack_type can only be one of IPV4_ONLY or IPV4_IPV6. Kasm recommends IPV4_ONLY since IPv6 is supported, but it requires additional, custom configuration by the administrator. Only enable IPv6 if you know what you are doing. Refer to this write up for more information: https://www.reddit.com/r/kasmweb/comments/sg6tv9/guide_enabling_ipv6_on_your_kasmweb_server/."
}
}
variable "enable_secure_boot" {
description = "Enable instance secure boot"
type = bool
default = null
}
variable "enable_instance_vtpm" {
description = "Enable the Trusted Platform Module to secure the instance"
type = bool
default = true
}
variable "enable_integrity_monitoring" {
description = "Enable instance integrity monitoring"
type = bool
default = true
}
variable "public_access_config" {
description = "Enable public IP access for instance"
type = list(object({
nat_ip = optional(string)
public_ptr_domain_name = optional(string)
network_tier = optional(string)
}))
}
variable "service_account" {
description = "Service account to use for instance auto updates"
type = list(object({
email = optional(string)
scopes = list(string)
}))
default = []
}
variable "resource_labels" {
description = "Labels to apply to the instance"
type = map(any)
default = null
}

View file

@ -0,0 +1,37 @@
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.0 |
| <a name="requirement_google"></a> [google](#requirement\_google) | ~> 4.0 |
## Providers
| Name | Version |
|------|---------|
| <a name="provider_google"></a> [google](#provider\_google) | 4.81.0 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [google_dns_record_set.records](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/dns_record_set) | resource |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_domain"></a> [domain](#input\_domain) | Zone domain, must end with a period. | `string` | n/a | yes |
| <a name="input_name"></a> [name](#input\_name) | Zone name, must be unique within the project. | `string` | n/a | yes |
| <a name="input_project_id"></a> [project\_id](#input\_project\_id) | Project id for the zone. | `string` | n/a | yes |
| <a name="input_recordsets"></a> [recordsets](#input\_recordsets) | List of DNS record objects to manage, in the standard terraform dns structure. | <pre>list(object({<br> name = string<br> type = string<br> ttl = number<br> records = list(string)<br> }))</pre> | `[]` | no |
## Outputs
No outputs.
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

View file

@ -0,0 +1,11 @@
resource "google_dns_record_set" "records" {
project = var.project_id
managed_zone = var.name
for_each = { for record in var.recordsets : join("/", [record.name, record.type]) => record }
name = (each.value.name != "" ? "${each.value.name}.${var.domain}." : "${var.domain}.")
type = each.value.type
ttl = each.value.ttl
rrdatas = each.value.records
}

View file

@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}

View file

@ -0,0 +1,25 @@
variable "project_id" {
description = "Project id for the zone."
type = string
}
variable "domain" {
description = "Zone domain, must end with a period."
type = string
}
variable "name" {
description = "Zone name, must be unique within the project."
type = string
}
variable "recordsets" {
type = list(object({
name = string
type = string
ttl = number
records = list(string)
}))
description = "List of DNS record objects to manage, in the standard terraform dns structure."
default = []
}

View file

@ -0,0 +1,60 @@
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.0 |
| <a name="requirement_google"></a> [google](#requirement\_google) | ~> 4.0 |
## Providers
| Name | Version |
|------|---------|
| <a name="provider_google"></a> [google](#provider\_google) | 4.81.0 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [google_compute_forwarding_rule.default](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_forwarding_rule) | resource |
| [google_compute_region_backend_service.default](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_region_backend_service) | resource |
| [google_compute_region_health_check.tcp](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_region_health_check) | resource |
| [google_compute_region_target_tcp_proxy.default](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_region_target_tcp_proxy) | resource |
| [google_compute_network.network](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/compute_network) | data source |
| [google_compute_subnetwork.network](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/compute_subnetwork) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_backend_timeout_sec"></a> [backend\_timeout\_sec](#input\_backend\_timeout\_sec) | Backend timeout | `number` | `30` | no |
| <a name="input_backends"></a> [backends](#input\_backends) | List of backends, should be a map of key-value pairs for each backend, must have the 'group' key. | `list(any)` | n/a | yes |
| <a name="input_connection_draining_timeout_sec"></a> [connection\_draining\_timeout\_sec](#input\_connection\_draining\_timeout\_sec) | Time for which instance will be drained | `number` | `300` | no |
| <a name="input_global_access"></a> [global\_access](#input\_global\_access) | Allow all regions on the same VPC network access. | `bool` | `false` | no |
| <a name="input_health_check"></a> [health\_check](#input\_health\_check) | Health check to determine whether instances are responsive and able to do work | <pre>object({<br> type = string<br> check_interval_sec = number<br> healthy_threshold = number<br> timeout_sec = number<br> unhealthy_threshold = number<br> response = string<br> proxy_header = string<br> port = number<br> port_name = string<br> request = string<br> request_path = string<br> host = string<br> enable_log = bool<br> })</pre> | n/a | yes |
| <a name="input_ip_protocol"></a> [ip\_protocol](#input\_ip\_protocol) | The IP protocol for the backend and frontend forwarding rule. TCP or UDP. | `string` | `"TCP"` | no |
| <a name="input_labels"></a> [labels](#input\_labels) | The labels to attach to resources created by this module. | `map(string)` | `{}` | no |
| <a name="input_load_balancing_policy"></a> [load\_balancing\_policy](#input\_load\_balancing\_policy) | The load balancing policy to use | `string` | `"ROUND_ROBIN"` | no |
| <a name="input_load_balancing_scheme"></a> [load\_balancing\_scheme](#input\_load\_balancing\_scheme) | The load balancing scheme to use. The default is INTERNAL | `string` | `"INTERNAL_MANAGED"` | no |
| <a name="input_name"></a> [name](#input\_name) | Name for the forwarding rule and prefix for supporting resources. | `string` | n/a | yes |
| <a name="input_named_port"></a> [named\_port](#input\_named\_port) | Named port to allow access to the LB or resources through the VPC FW | `string` | n/a | yes |
| <a name="input_network"></a> [network](#input\_network) | Name of the network to create resources in. | `string` | `"default"` | no |
| <a name="input_network_project"></a> [network\_project](#input\_network\_project) | Name of the project for the network. Useful for shared VPC. Default is var.project. | `string` | `""` | no |
| <a name="input_port_range"></a> [port\_range](#input\_port\_range) | List of ports range to forward to backend services. Max is 5. | `string` | n/a | yes |
| <a name="input_project"></a> [project](#input\_project) | The project to deploy to, if not set the default provider project is used. | `string` | `""` | no |
| <a name="input_region"></a> [region](#input\_region) | Region for cloud resources. | `string` | n/a | yes |
| <a name="input_session_affinity"></a> [session\_affinity](#input\_session\_affinity) | The session affinity for the backends example: NONE, CLIENT\_IP. Default is `NONE`. | `string` | `"NONE"` | no |
| <a name="input_subnetwork"></a> [subnetwork](#input\_subnetwork) | Name of the subnetwork to create resources in. | `string` | `"default"` | no |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_forwarding_rule"></a> [forwarding\_rule](#output\_forwarding\_rule) | The forwarding rule self\_link. |
| <a name="output_forwarding_rule_id"></a> [forwarding\_rule\_id](#output\_forwarding\_rule\_id) | The forwarding rule id. |
| <a name="output_ip_address"></a> [ip\_address](#output\_ip\_address) | The internal IP assigned to the regional forwarding rule. |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

View file

@ -0,0 +1,81 @@
data "google_compute_network" "network" {
name = var.network
project = var.network_project == "" ? var.project : var.network_project
}
data "google_compute_subnetwork" "network" {
name = var.subnetwork
project = var.network_project == "" ? var.project : var.network_project
region = var.region
}
resource "google_compute_forwarding_rule" "default" {
project = var.project
name = var.name
region = var.region
network = data.google_compute_network.network.self_link
subnetwork = data.google_compute_subnetwork.network.self_link
allow_global_access = var.global_access
load_balancing_scheme = var.load_balancing_scheme
network_tier = "PREMIUM"
ip_protocol = var.ip_protocol
port_range = var.port_range
labels = var.labels
target = google_compute_region_target_tcp_proxy.default.self_link
}
resource "google_compute_region_target_tcp_proxy" "default" {
region = var.region
name = "${var.name}-target-proxy"
proxy_header = "NONE"
backend_service = google_compute_region_backend_service.default.self_link
}
resource "google_compute_region_backend_service" "default" {
project = var.project
name = "${var.name}-backend-service"
region = var.region
protocol = var.ip_protocol
locality_lb_policy = var.load_balancing_policy
port_name = var.named_port
session_affinity = var.session_affinity
timeout_sec = var.backend_timeout_sec
load_balancing_scheme = "INTERNAL_MANAGED"
connection_draining_timeout_sec = var.connection_draining_timeout_sec
dynamic "backend" {
for_each = var.backends
content {
group = lookup(backend.value, "group", null)
description = lookup(backend.value, "description", null)
max_utilization = lookup(backend.value, "max_utilization", null)
balancing_mode = lookup(backend.value, "balancing_mode", null)
capacity_scaler = lookup(backend.value, "capacity_scaler", null)
failover = lookup(backend.value, "failover", null)
}
}
health_checks = [google_compute_region_health_check.tcp.self_link]
}
resource "google_compute_region_health_check" "tcp" {
project = var.project
name = "${var.name}-hc-tcp"
region = var.region
timeout_sec = var.health_check["timeout_sec"]
check_interval_sec = var.health_check["check_interval_sec"]
healthy_threshold = var.health_check["healthy_threshold"]
unhealthy_threshold = var.health_check["unhealthy_threshold"]
https_health_check {
port = var.health_check["port"]
port_name = var.health_check["port_name"]
request_path = var.health_check["request_path"]
proxy_header = var.health_check["proxy_header"]
}
dynamic "log_config" {
for_each = var.health_check["enable_log"] ? [true] : []
content {
enable = true
}
}
}

View file

@ -0,0 +1,14 @@
output "ip_address" {
description = "The internal IP assigned to the regional forwarding rule."
value = google_compute_forwarding_rule.default.ip_address
}
output "forwarding_rule" {
description = "The forwarding rule self_link."
value = google_compute_forwarding_rule.default.self_link
}
output "forwarding_rule_id" {
description = "The forwarding rule id."
value = google_compute_forwarding_rule.default.id
}

View file

@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}

View file

@ -0,0 +1,115 @@
variable "project" {
description = "The project to deploy to, if not set the default provider project is used."
type = string
default = ""
}
variable "region" {
description = "Region for cloud resources."
type = string
}
variable "global_access" {
description = "Allow all regions on the same VPC network access."
type = bool
default = false
}
variable "network" {
description = "Name of the network to create resources in."
type = string
default = "default"
}
variable "subnetwork" {
description = "Name of the subnetwork to create resources in."
type = string
default = "default"
}
variable "network_project" {
description = "Name of the project for the network. Useful for shared VPC. Default is var.project."
type = string
default = ""
}
variable "name" {
description = "Name for the forwarding rule and prefix for supporting resources."
type = string
}
variable "backends" {
description = "List of backends, should be a map of key-value pairs for each backend, must have the 'group' key."
type = list(any)
}
variable "backend_timeout_sec" {
description = "Backend timeout"
type = number
default = 30
}
variable "load_balancing_scheme" {
description = "The load balancing scheme to use. The default is INTERNAL"
type = string
default = "INTERNAL_MANAGED"
}
variable "load_balancing_policy" {
description = "The load balancing policy to use"
type = string
default = "ROUND_ROBIN"
}
variable "named_port" {
description = "Named port to allow access to the LB or resources through the VPC FW"
type = string
}
variable "session_affinity" {
description = "The session affinity for the backends example: NONE, CLIENT_IP. Default is `NONE`."
type = string
default = "NONE"
}
variable "port_range" {
description = "List of ports range to forward to backend services. Max is 5."
type = string
}
variable "health_check" {
description = "Health check to determine whether instances are responsive and able to do work"
type = object({
type = string
check_interval_sec = number
healthy_threshold = number
timeout_sec = number
unhealthy_threshold = number
response = string
proxy_header = string
port = number
port_name = string
request = string
request_path = string
host = string
enable_log = bool
})
}
variable "ip_protocol" {
description = "The IP protocol for the backend and frontend forwarding rule. TCP or UDP."
type = string
default = "TCP"
}
variable "connection_draining_timeout_sec" {
description = "Time for which instance will be drained"
default = 300
type = number
}
variable "labels" {
description = "The labels to attach to resources created by this module."
default = {}
type = map(string)
}

View file

@ -0,0 +1,36 @@
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.0 |
| <a name="requirement_random"></a> [random](#requirement\_random) | ~> 3.0 |
## Providers
| Name | Version |
|------|---------|
| <a name="provider_random"></a> [random](#provider\_random) | 3.5.1 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [random_password.password](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_kasm_version"></a> [kasm\_version](#input\_kasm\_version) | The version of kasm installed | `string` | n/a | yes |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_password"></a> [password](#output\_password) | # ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## # Auto-generated passwords # ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

View file

@ -0,0 +1,7 @@
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
## Auto-generated passwords
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
output "password" {
value = random_password.password.result
sensitive = true
}

View file

@ -0,0 +1,8 @@
resource "random_password" "password" {
keepers = {
kasm_version = var.kasm_version
}
length = 30
special = false
}

View file

@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.0"
required_providers {
random = {
source = "hashicorp/random"
version = "~> 3.0"
}
}
}

View file

@ -0,0 +1,4 @@
variable "kasm_version" {
description = "The version of kasm installed"
type = string
}

View file

@ -0,0 +1,44 @@
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.0 |
| <a name="requirement_google"></a> [google](#requirement\_google) | ~> 4.0 |
## Providers
| Name | Version |
|------|---------|
| <a name="provider_google"></a> [google](#provider\_google) | 4.81.0 |
## Modules
| Name | Source | Version |
|------|--------|---------|
| <a name="module_dns_zone_admin"></a> [dns\_zone\_admin](#module\_dns\_zone\_admin) | terraform-google-modules/iam/google//modules/dns_zones_iam | ~> 7.6 |
| <a name="module_service_account_roles"></a> [service\_account\_roles](#module\_service\_account\_roles) | terraform-google-modules/iam/google//modules/member_iam | ~> 7.6 |
## Resources
| Name | Type |
|------|------|
| [google_service_account.service_account](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account) | resource |
| [google_service_account_key.kasm_key](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/service_account_key) | resource |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_account_id"></a> [account\_id](#input\_account\_id) | The account id used to generate the service account email address | `string` | n/a | yes |
| <a name="input_display_name"></a> [display\_name](#input\_display\_name) | The service account display name | `string` | `""` | no |
| <a name="input_dns_public_zone_name"></a> [dns\_public\_zone\_name](#input\_dns\_public\_zone\_name) | Friendly name of the public DNS zone to manage. Only used if Direct to Agent is enabled. | `string` | `""` | no |
| <a name="input_manage_dns"></a> [manage\_dns](#input\_manage\_dns) | Allow the service account to add/delete DNS records for direct-to-agent. | `bool` | `false` | no |
| <a name="input_project_id"></a> [project\_id](#input\_project\_id) | Project id for the zone. | `string` | n/a | yes |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_connect_details"></a> [connect\_details](#output\_connect\_details) | Kasm autoscale service account connect JSON output. NOTE: This contains sensitive data! |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

View file

@ -0,0 +1,8 @@
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
## Service Account details
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
output "connect_details" {
description = "Kasm autoscale service account connect JSON output. NOTE: This contains sensitive data!"
value = google_service_account_key.kasm_key.private_key
sensitive = true
}

View file

@ -0,0 +1,9 @@
terraform {
required_version = "~> 1.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
}
}

View file

@ -0,0 +1,34 @@
resource "google_service_account" "service_account" {
project = var.project_id
account_id = var.account_id
display_name = var.display_name
}
resource "google_service_account_key" "kasm_key" {
service_account_id = google_service_account.service_account.id
}
module "service_account_roles" {
source = "terraform-google-modules/iam/google//modules/member_iam"
version = "~> 7.6"
service_account_address = google_service_account.service_account.email
project_id = var.project_id
project_roles = ["roles/compute.instanceAdmin", "roles/iam.serviceAccountUser"]
prefix = "serviceAccount"
}
module "dns_zone_admin" {
source = "terraform-google-modules/iam/google//modules/dns_zones_iam"
version = "~> 7.6"
count = var.manage_dns ? 1 : 0
project = var.project_id
managed_zones = [var.dns_public_zone_name]
mode = "additive"
bindings = {
"roles/dns.admin" = [
"serviceAccount:${google_service_account.service_account.email}"
]
}
}

View file

@ -0,0 +1,33 @@
variable "project_id" {
description = "Project id for the zone."
type = string
}
variable "account_id" {
description = "The account id used to generate the service account email address"
type = string
validation {
condition = can(regex("^([a-z]([-a-z0-9]*[a-z0-9]){6,30})", var.account_id))
error_message = "The account_id must be between 6-30 characters beginning with a lower case letter, and consisting of lower case letters, numbers, or dash (-)."
}
}
## Pre-set values
variable "display_name" {
description = "The service account display name"
type = string
default = ""
}
variable "dns_public_zone_name" {
description = "Friendly name of the public DNS zone to manage. Only used if Direct to Agent is enabled."
type = string
default = ""
}
variable "manage_dns" {
description = "Allow the service account to add/delete DNS records for direct-to-agent."
type = bool
default = false
}

21
gcp/outputs.tf Normal file
View file

@ -0,0 +1,21 @@
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
## Service Account details
## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
output "kasm_sa_account" {
description = "Kasm Service Account connection details"
value = var.show_sa_credentials ? module.kasm_autoscale_service_account[*].connect_details : null
sensitive = true
}
output "kasm_passwords" {
description = "Kasm login passwords"
value = var.show_passwords ? {
kasm_admin_password = local.admin_password
kasm_user_password = local.user_password
kasm_database_password = local.database_password
kasm_redis_password = local.redis_password
kasm_service_token = local.service_token
kasm_manager_token = local.manager_token
} : null
sensitive = true
}

31
gcp/provider.tf Normal file
View file

@ -0,0 +1,31 @@
terraform {
required_version = "~> 1.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.0"
}
google-beta = {
source = "hashicorp/google-beta"
version = ">= 4.64, < 6"
}
random = {
source = "hashicorp/random"
version = "~> 3.0"
}
tls = {
source = "hashicorp/tls"
version = "~> 2.0"
}
}
}
provider "google" {
project = var.project_id
credentials = local.gcp_credentials
}
provider "google-beta" {
project = var.project_id
credentials = local.gcp_credentials
}

69
gcp/terraform.tfvars Normal file
View file

@ -0,0 +1,69 @@
## Connection variables
project_id = ""
google_credential_file_path = "./gcp_credentials.json"
## VPC and deployment environment variables
vpc_name = ""
kasm_vpc_subnet = "10.0.0.0/16"
## Ensure the desired Database region is the first value in the list
kasm_deployment_regions = ["us-east1"] # Use only one region for Multi-Server (single-region)
#kasm_deployment_regions = ["us-west2", "asia-southeast1"] # Use multiple regions for Multi-Region deployment
## DNS Zone settings
create_public_dns_zone = true
public_dns_friendly_name = "kasm-public-dns-zone"
private_dns_friendly_name = "kasm-private-dns-zone"
## Additional Kasm services or GCP features to deploy
create_kasm_autoscale_service_account = true
service_account_name = "kasm-autoscale"
show_passwords = true
## Kasm variables
kasm_domain_name = "example.kasmweb.com"
kasm_project_name = ""
deployment_type = "Multi-Region" # Valid values Multi-Region or Multi-Server
kasm_version = "1.15.0"
kasm_download_url = "https://github.com/kasmtech/kasm-install-wizard/releases/download/1.15.0/kasm_release.tar.gz"
## Kasm VM instance configurations
# Webapp
webapp_vm_instance_config = {
machine_type = "e2-standard-2"
disk_size_gb = "50"
disk_type = "pd-standard"
}
# Database
database_vm_instance_config = {
name = "kasm-database"
machine_type = "e2-standard-2"
disk_size_gb = 50
description = "Kasm database with Terraform"
disk_auto_delete = true
disk_type = "pd-balanced"
instance_role = "database"
}
# Agent
number_of_agents_per_region = 1
enable_agent_nat_gateway = false
agent_vm_instance_config = {
name_prefix = "kasm-static-agent"
machine_type = "e2-standard-2"
disk_size_gb = 100
description = "Kasm Static agent with Terraform"
disk_auto_delete = true
disk_type = "pd-balanced"
instance_role = "agent"
}
# Connection Proxy (Guac)
deploy_connection_proxy = false
deploy_windows_hosts = false ## NOTE: This only creates the Windows subnet and firewall rules, it does NOT deploy Windows VMs
cpx_vm_instance_config = {
machine_type = "e2-standard-2"
disk_size_gb = "50"
disk_type = "pd-standard"
}

View file

@ -0,0 +1,61 @@
#!/bin/bash
## Download Kasm
cd /tmp
wget ${KASM_DOWNLOAD_URL}
tar xvf kasm_*.tar.gz
## Create Swap partition
fallocate -l 8g /mnt/kasm.swap
chmod 600 /mnt/kasm.swap
mkswap /mnt/kasm.swap
swapon /mnt/kasm.swap
echo '/mnt/kasm.swap swap swap defaults 0 0' | tee -a /etc/fstab
apt update && apt install iputils-ping dnsutils netcat -y
## Verify connectivity with the webapp private load balancer
while ! (curl -k https://${PRIVATE_LB_HOSTNAME}/api/__healthcheck 2>/dev/null | grep -q true)
do
echo "Waiting for API server..."
sleep 5
done
## Get Agent Private IP for Kasm registration
connect_ip="`hostname -I | cut -d' ' -f1`"
## Install Kasm
## Kasm install arguments used:
## -S = Kasm role - agent in this case
## -H = Don't check for swap (since we created it already)
## -e = accept EULA
## -p = Agent IP address or hostname
## -m = Webapp private load balancer hostname
## -M = Manager token to use to register the agent
## Useful additional arguments:
## -O = use Rolling images (ensures the most up-to-date containers are used)
bash kasm_release/install.sh -S agent -H -e -p $${connect_ip} -m ${PRIVATE_LB_HOSTNAME} -M ${KASM_MANAGER_TOKEN} ${ADDITIONAL_AGENT_INSTALL_ARGS}
## Install Nvidia drivers if this is a GPU-enabled agent.
if [[ "${GPU_ENABLED}" == "1" ]]
then
apt-get update && apt-get upgrade -y
apt-get install -y gcc make linux-headers-$(uname -r) linux-aws awscli
cat << EOF | tee --append /etc/modprobe.d/blacklist.conf
blacklist vga16fb
blacklist nouveau
blacklist rivafb
blacklist nvidiafb
blacklist rivatv
EOF
echo 'GRUB_CMDLINE_LINUX="rdblacklist=nouveau"' | tee --append /etc/default/grub
update-grub
aws s3 cp --no-sign-request --recursive s3://ec2-linux-nvidia-drivers/latest/ .
chmod +x NVIDIA-Linux-x86_64*.run
/bin/sh ./NVIDIA-Linux-x86_64*.run -s
curl -fsSL "https://nvidia.github.io/nvidia-docker/gpgkey" | gpg --dearmor | tee /etc/apt/trusted.gpg.d/nvidia.gpg > /dev/null
curl -s -L "https://nvidia.github.io/nvidia-docker/$(source /etc/os-release;echo $ID$VERSION_ID)/nvidia-docker.list" -o /etc/apt/sources.list.d/nvidia-docker.list
apt-get update
apt-get install -y nvidia-docker2
systemctl restart docker
docker restart kasm_agent
fi

View file

@ -0,0 +1,39 @@
#!/bin/bash
## Download Kasm
cd /tmp
wget ${KASM_DOWNLOAD_URL}
tar xvf kasm_*.tar.gz
## Create Swap partition
fallocate -l 2g /mnt/kasm.swap
chmod 600 /mnt/kasm.swap
mkswap /mnt/kasm.swap
swapon /mnt/kasm.swap
echo '/mnt/kasm.swap swap swap defaults 0 0' | tee -a /etc/fstab
## Install useful packages
apt update && apt install iputils-ping dnsutils netcat -y
## Make sure the CPX node can access the private load balancer
while ! (curl -k https://${PRIVATE_LB_HOSTNAME}/api/__healthcheck 2>/dev/null | grep -q true)
do
echo "Waiting for API server..."
sleep 5
done
## Get CPX Private IP for Kasm registration
connect_ip="`hostname -I | cut -d' ' -f1`"
## Install Kasm
## Kasm install arguments used:
## -S = Kasm role - guac in this case
## -H = Don't check for swap (since we created it already)
## -e = accept EULA
## -n = Private Load balancer URL for Kasm webapps
## -p = CPX Node private IP so webapp can connect to CPX
## -k = Service registration token required to register the CPX with Kasm
## -z = The Zone name to register the CPX node with
## Useful additional arguments:
## -O = use Rolling images (ensures the most up-to-date containers are used)
bash kasm_release/install.sh -S guac -H -e -n ${PRIVATE_LB_HOSTNAME} -p $${connect_ip} -k ${KASM_SERVICE_TOKEN} -z ${KASM_ZONE_NAME} ${ADDITIONAL_CPX_INSTALL_ARGS}

View file

@ -0,0 +1,31 @@
#!/bin/bash
## Download Kasm
cd /tmp
wget ${KASM_DOWNLOAD_URL}
tar xvf kasm_*.tar.gz
## Create swap partition
fallocate -l 8g /mnt/kasm.swap
chmod 600 /mnt/kasm.swap
mkswap /mnt/kasm.swap
swapon /mnt/kasm.swap
echo '/mnt/kasm.swap swap swap defaults 0 0' | tee -a /etc/fstab
## Install useful packages
apt update && apt install iputils-ping dnsutils netcat -y
## Install Kasm
## Kasm install arguments used:
## -S = Kasm role - db in this case
## -H = Don't check for swap (since we created it already)
## -e = accept EULA
## -Q = Database password
## -R = Redis password
## -U = Password to use for user@kasm.local built-in account
## -P = Password to use for admin@kasm.local built-in admin account
## -M = Management token to use for agent registration
## -k = Service registration token to use for Connection Proxy (Guac) registration
## Useful additional arguments:
## -O = use Rolling images (ensures the most up-to-date containers are used)
bash kasm_release/install.sh -S db -e -Q ${KASM_DB_PASS} -R ${KASM_REDIS_PASS} -U ${KASM_USER_PASS} -P ${KASM_ADMIN_PASS} -M ${KASM_MANAGER_TOKEN} -k ${KASM_SERVICE_TOKEN} ${ADDITIONAL_DATABASE_INSTALL_ARGS}

View file

@ -0,0 +1,48 @@
#!/bin/bash
## Download Kasm
cd /tmp
wget ${KASM_DOWNLOAD_URL}
tar xvf kasm_*.tar.gz
## Create swap partition
fallocate -l 2g /mnt/kasm.swap
chmod 600 /mnt/kasm.swap
mkswap /mnt/kasm.swap
swapon /mnt/kasm.swap
echo '/mnt/kasm.swap swap swap defaults 0 0' | tee -a /etc/fstab
## Install useful packages
apt update && apt install iputils-ping dnsutils netcat -y
## Ensure connection to remote database before installing
while ! nc -w 1 -z ${DATABASE_IP} 5432
do
echo "Waiting for DB connection..."
sleep 10
done
## Ensure connection to remote Redis before installing
while ! nc -w 1 -z ${REDIS_IP} 6379
do
echo "Waiting for Redis connection..."
sleep 10
done
## Install Kasm
## Kasm install arguments used:
## -S = Kasm role - init_remote_db in this case
## -H = Don't check for swap (since we created it already)
## -e = accept EULA
## -q = Database IP or Hostname
## -Q = Database password
## -o = Redis IP or Hostname
## -R = Redis password
## -U = Password to use for user@kasm.local built-in account
## -P = Password to use for admin@kasm.local built-in admin account
## -M = Management token to use for agent registration
## -k = Service registration token to use for Connection Proxy (Guac) registration
## Useful additional arguments:
## -O = use Rolling images (ensures the most up-to-date containers are used)
bash kasm_release/install_dependencies.sh
bash kasm_release/install.sh -S init_remote_db -e -H -q ${DATABASE_IP} -Q ${KASM_DB_PASS} -U ${KASM_USER_PASS} -P ${KASM_ADMIN_PASS} -o ${REDIS_IP} -R ${KASM_REDIS_PASS} -M ${KASM_SERVICE_TOKEN} -g ${DB_MASTER_USER} -G ${DB_MASTER_PASSWORD} -k ${KASM_SERVICE_TOKEN} ${ADDITIONAL_DATABASE_INSTALL_ARGS}

View file

@ -0,0 +1,43 @@
#!/bin/bash
## Download Kasm
cd /tmp
wget ${KASM_DOWNLOAD_URL}
tar xvf kasm_*.tar.gz
## Create Swap partition
fallocate -l 2g /mnt/kasm.swap
chmod 600 /mnt/kasm.swap
mkswap /mnt/kasm.swap
swapon /mnt/kasm.swap
echo '/mnt/kasm.swap swap swap defaults 0 0' | tee -a /etc/fstab
## Install useful packages
apt update && apt install iputils-ping dnsutils netcat -y
## Test Database connectivity before installing
while ! nc -w 1 -z ${DB_PRIVATE_IP} 5432
do
echo "Waiting for DB connection..."
sleep 5
done
## Test Redis connectivity before installing
while ! nc -w 1 -z ${DB_PRIVATE_IP} 6379
do
echo "Waiting for Redis connection..."
sleep 5
done
## Install Kasm
## Kasm install arguments used:
## -S = Kasm role - webapp in this case
## -H = Don't check for swap (since we created it already)
## -e = accept EULA
## -q = Database Server IP
## -Q = Database password
## -R = Redis password
## -z = The Zone name to use for the webapp
## Useful additional arguments:
## -O = use Rolling images (ensures the most up-to-date containers are used)
bash kasm_release/install.sh -S app -H -e -z ${KASM_ZONE_NAME} -q ${DB_PRIVATE_IP} -Q ${KASM_DB_PASS} -R ${KASM_REDIS_PASS} ${ADDITIONAL_WEBAPP_INSTALL_ARGS}

852
gcp/variables.tf Normal file
View file

@ -0,0 +1,852 @@
## Connection variables
variable "project_id" {
description = "GCP Project ID where to deploy Kasm"
type = string
validation {
condition = can(regex("^[a-z0-9-]", var.project_id))
error_message = "The project_id variable should be a valid GCP Project ID and can only consist of lower case letters, numbers, and dash (-)."
}
}
variable "google_credential_file_path" {
description = "File path to GCP account authentication file"
type = string
default = ""
validation {
condition = var.google_credential_file_path == "" ? true : fileexists(var.google_credential_file_path)
error_message = "The variable google_credential_file_path must point to a valid GCP credential file."
}
validation {
condition = var.google_credential_file_path == "" ? true : !can(regex("replaceme", file(var.google_credential_file_path)))
error_message = "You must add valid GCP credentials in JSON format in the google_credential_file_path file."
}
}
## Network variables
variable "vpc_name" {
description = "Name for Kasm VPC"
type = string
validation {
condition = can(regex("^[a-z][a-z0-9-]{3,63}", var.vpc_name))
error_message = "The vpc_name variable can only be a max of 63 characters beginning with a lower case letter, and consisting of lower case letters, numbers, and dash (-)."
}
}
variable "kasm_vpc_subnet" {
description = "VPC Subnet CIDR range. All other Subnets will be automatically calculated from this seed value."
type = string
default = "10.0.0.0/16"
validation {
condition = can(cidrhost(var.kasm_vpc_subnet, 0))
error_message = "The kasm_vpc_subnet variable must be a valid IPv4 CIDR Subnet in the format x.x.x.x/x, and the default is 10.0.0.0/16."
}
}
variable "kasm_deployment_regions" {
description = "Kasm regions to deploy into"
type = list(string)
}
## Kasm variables
variable "kasm_download_url" {
description = "Download URL for Kasm Workspaces installer"
type = string
}
variable "kasm_domain_name" {
description = "Public DNS domain name to use for Kasm deployment"
type = string
validation {
condition = can(regex("^[a-z0-9]+([\\-\\.]{1}[a-z0-9]+)*\\.[a-z]{2,6}", var.kasm_domain_name))
error_message = "There are invalid characters in the kasm_domain_name - it must be a valid domain name."
}
}
variable "kasm_project_name" {
description = "Kasm deployment project name (separate from GCP Project id or Project Name)"
type = string
default = ""
validation {
condition = var.kasm_project_name == "" ? true : can(regex("^[a-z0-9-]{2,25}", var.kasm_project_name))
error_message = "The kasm_project_name is used in labels and "
}
}
variable "deployment_type" {
description = "The deployment type - Single-Server, Multi-Server, or Multi-Region"
type = string
default = "Multi-Server"
validation {
condition = contains(["Single-Server", "Multi-Server", "Multi-Region", ""], var.deployment_type)
error_message = "The deployment_type variable declares the deployment type and can only consisit of one of the values Single-Server, Multi-Server, or Multi-Region."
}
}
variable "kasm_version" {
description = "Kasm version to deploy"
type = string
default = ""
}
variable "kasm_database_password" {
description = "The password for the database. No special characters"
type = string
sensitive = true
default = ""
validation {
condition = var.kasm_database_password == "" ? true : can(regex("^[a-zA-Z0-9]{12,40}", var.kasm_database_password))
error_message = "The Kasm Database should be a string between 12 and 40 letters or numbers with no special characters."
}
}
variable "kasm_redis_password" {
description = "The password for the Redis server. No special characters"
type = string
sensitive = true
default = ""
validation {
condition = var.kasm_redis_password == "" ? true : can(regex("^[a-zA-Z0-9]{12,40}", var.kasm_redis_password))
error_message = "The Kasm Redis should be a string between 12 and 40 letters or numbers with no special characters."
}
}
variable "kasm_user_password" {
description = "The standard (non administrator) user password. No special characters"
type = string
sensitive = true
default = ""
validation {
condition = var.kasm_user_password == "" ? true : can(regex("^[a-zA-Z0-9]{12,40}", var.kasm_user_password))
error_message = "The Kasm User should be a string between 12 and 40 letters or numbers with no special characters."
}
}
variable "kasm_admin_password" {
description = "The administrative user password. No special characters"
type = string
sensitive = true
default = ""
validation {
condition = var.kasm_admin_password == "" ? true : can(regex("^[a-zA-Z0-9]{12,40}", var.kasm_admin_password))
error_message = "The Kasm Admin should be a string between 12 and 40 letters or numbers with no special characters."
}
}
variable "kasm_manager_token" {
description = "The manager token value for Agents to authenticate to webapps. No special characters"
type = string
sensitive = true
default = ""
validation {
condition = var.kasm_manager_token == "" ? true : can(regex("^[a-zA-Z0-9]{12,40}", var.kasm_manager_token))
error_message = "The Manager Token should be a string between 12 and 40 letters or numbers with no special characters."
}
}
variable "kasm_service_token" {
description = "The service registration token value for Guac RDP servers to authenticate to webapps. No special characters"
type = string
sensitive = true
default = ""
validation {
condition = var.kasm_service_token == "" ? true : can(regex("^[a-zA-Z0-9]{12,40}", var.kasm_service_token))
error_message = "The Service Registration Token should be a string between 12 and 40 letters or numbers with no special characters."
}
}
variable "show_passwords" {
description = "Show Kasm passwords in root Terraform output"
type = bool
default = true
}
variable "show_sa_credentials" {
description = "Show GCP Service account credential file in output"
type = bool
default = true
}
## SSL Certificate variables
variable "use_gcp_certificate_manager" {
description = "Use Certificate Manager to create and manage the Kasm public SSL certificate"
type = bool
default = false
}
variable "kasm_certificate_base_name" {
description = "Name to use for Kasm Global SSL certificate"
type = string
default = "kasm-global-tls-certificate"
validation {
condition = can(regex("^[a-z0-9-]{4,63}", var.kasm_certificate_base_name))
error_message = "The kasm_certificate_base_name variable can only be a max of 63 characters consisting of lower case letters, numbers, and dash (-)."
}
}
variable "kasm_certificate_dns_auth_base_name" {
description = "Name to use for Kasm SSL DNS authorization service"
type = string
default = "kasm-global-certificate-dns-authorization"
validation {
condition = can(regex("^[a-z][a-z0-9-]{3,63}", var.kasm_certificate_dns_auth_base_name))
error_message = "The kasm_certificate_dns_auth_base_name variable can only be a max of 63 characters beginning with a lower case letter, and consisting of lower case letters, numbers, and dash (-)."
}
}
variable "kasm_cert_map_base_name" {
description = "Name to use for Kasm Global SSL certificate map"
type = string
default = "kasm-global-certificate-map"
validation {
condition = can(regex("^[a-z][a-z0-9-]{3,63}", var.kasm_cert_map_base_name))
error_message = "The kasm_cert_map_base_name variable can only be a max of 63 characters beginning with a lower case letter, and consisting of lower case letters, numbers, and dash (-)."
}
}
## Webapp variables
variable "webapp_vm_instance_config" {
description = "Webapp Compute instance configuration settings"
type = object({
machine_type = string
disk_size_gb = string
disk_type = string
})
validation {
condition = tonumber(var.webapp_vm_instance_config.disk_size_gb) > 30
error_message = "The webapp_vm_instance_config disk_size_gb should be larger than 30GB to support Kasm and other required services. Recommended minimum size is 50GB."
}
validation {
condition = contains(["pd-standard", "pd-ssd", "local-ssd", "pd-extreme"], var.webapp_vm_instance_config.disk_type)
error_message = "The webapp_vm_instance_config disk_type attribute can only be one of pd-standard, pd-ssd, pd-extreme, or local-ssd."
}
}
variable "webapp_named_ports" {
description = "Webapp named ports for firewall and Google service connectivity"
type = list(object({
name = string
port = number
}))
default = [
{
name = "https"
port = 443
}
]
}
variable "webapp_hostname_prefix" {
description = "Webapp hostname prefix to use for instance group"
type = string
default = "webapp"
validation {
condition = can(regex("^[a-z][a-z0-9-]{3,63}", var.webapp_hostname_prefix))
error_message = "The webapp_hostname_prefix variable can only be a max of 63 characters beginning with a lower case letter, and consisting of lower case letters, numbers, and dash (-)."
}
}
variable "webapp_autoscale_min_instances" {
description = "Webapp Autoscale minimum number of instances"
type = number
default = 2
validation {
condition = var.webapp_autoscale_min_instances >= 1
error_message = "The webapp_autoscale_min_instances should be 3 at a minimum since that is the typical number of availability domains for a region."
}
}
variable "webapp_autoscale_max_instances" {
description = "Webapp Autoscale maximum number of instances"
type = number
default = 5
validation {
condition = var.webapp_autoscale_max_instances >= 1
error_message = "The webapp_autoscale_max_instances should be larger than the webapp_autoscale_min_instances value."
}
}
variable "webapp_autoscale_cool_down_period" {
description = "Time in seconds for the autoscale group to wait before evaluating the health of the webapp"
type = number
default = 600
validation {
condition = var.webapp_autoscale_cool_down_period >= 300
error_message = "The webapp_autoscale_cool_down_periodc should be greater than 300 to allow webapps to be fully available before being evaluated with health checks. Kasm recommends around 600 or more."
}
}
variable "webapp_autoscale_scale_out_cpu" {
description = "Webapp Autoscale CPU percent to scale up webapps"
type = list(object({
target = number
predictive_method = string
}))
default = [{
target = 0.6
predictive_method = "NONE"
}]
validation {
condition = alltrue([for value in var.webapp_autoscale_scale_out_cpu : value.target >= 0.5])
error_message = "The webapp_autoscale_scale_out_cpu target attribute should be 0.5 at a minimum to prevent GCP from scaling webapps too aggressively."
}
validation {
condition = alltrue([for value in var.webapp_autoscale_scale_out_cpu : contains(["NONE", "OPTIMIZE_AVAILABILITY"], value.predictive_method)])
error_message = "The webapp_autoscale_scale_out_cpu predictive_method attribute can only be one of NONE or OPTIMIZE_AVAILABILITY."
}
}
variable "webapp_autoscale_scale_in_settings" {
description = "Webapp Autoscale scale-in settings"
type = object({
fixed_replicas = number
time_window_sec = number
percent_replicas = optional(number, null)
})
default = {
fixed_replicas = 1
time_window_sec = 600
}
validation {
condition = var.webapp_autoscale_scale_in_settings.fixed_replicas >= 1
error_message = "The webapp_instance_group_scale_settings fixed_replicas should be 1 or more."
}
validation {
condition = var.webapp_autoscale_scale_in_settings.time_window_sec >= 300
error_message = "The webapp_instance_group_scale_settings time_window_sec should be greater than 300 to allow webapps to be fully available before being evaluated with health checks. Kasm recommends around 600 or more."
}
}
variable "webapp_health_check_name" {
description = "Name of Webapp Managed Instance Group healthcheck"
type = string
default = "webapp-healthcheck"
validation {
condition = can(regex("^[a-z][a-z0-9-]{3,63}", var.webapp_health_check_name))
error_message = "The webapp_health_check_name variable can only be a max of 63 characters beginning with a lower case letter, and consisting of lower case letters, numbers, and dash (-)."
}
}
variable "webapp_health_check" {
description = "HTTPS Managed Instance Group healthcheck for webapps."
type = object({
type = string
initial_delay_sec = number
check_interval_sec = number
healthy_threshold = number
timeout_sec = number
unhealthy_threshold = number
port = number
port_name = string
request_path = string
response = optional(string, "")
proxy_header = optional(string, "NONE")
request = optional(string, "")
host = optional(string, "")
enable_log = optional(bool, false)
enable_logging = optional(string, false)
})
default = {
type = "https"
initial_delay_sec = 600
check_interval_sec = 30
healthy_threshold = 2
timeout_sec = 10
unhealthy_threshold = 5
port = 443
port_name = "https"
request_path = "/api/__healthcheck"
}
}
variable "webapp_instance_update_policy" {
description = "The Instance group rolling update policy"
type = list(object({
instance_redistribution_type = string
min_ready_sec = number
replacement_method = string
minimal_action = string
type = string
max_surge_fixed = optional(number, null)
max_surge_percent = optional(number, null) # Can only use if you run 10 or more instances
max_unavailable_fixed = optional(number, null)
max_unavailable_percent = optional(number, null) # Can only use if you run 10 or more instances
}))
default = [{
minimal_action = "REFRESH"
type = "PROACTIVE" # Update automatically
max_surge_fixed = 3 # Max new instances to startup
max_unavailable_fixed = 0 # Max instances to take offline before new ones available
instance_redistribution_type = "PROACTIVE" # Maintain even distribution across availability zones
replacement_method = "SUBSTITUTE" # Start new ones before taking any offline
min_ready_sec = 600 # Wait time before instance is ready
}]
validation {
condition = alltrue([for value in var.webapp_instance_update_policy : contains(["PROACTIVE", "OPPORTUNISTIC"], value.type)])
error_message = "The webapp_instance_update_policy type setting only accepts PROACTIVE or OPPORTUNISTIC."
}
validation {
condition = alltrue([for value in var.webapp_instance_update_policy : contains(["PROACTIVE", "OPPORTUNISTIC"], value.instance_redistribution_type)])
error_message = "The webapp_instance_update_policy instance_redistribution_type setting only accepts PROACTIVE or OPPORTUNISTIC."
}
validation {
condition = alltrue([for value in var.webapp_instance_update_policy : contains(["RECREATE", "SUBSTITUTE"], value.replacement_method)])
error_message = "The webapp_instance_update_policy replacement_method setting only accepts RECREATE or SUBSTITUTE."
}
validation {
condition = alltrue([for value in var.webapp_instance_update_policy : value.min_ready_sec >= 300])
error_message = "The webapp_instance_update_policy min_ready_sec should be greater than or equal to 300 seconds (5 min). Ideally, this should be around 600 seconds (10 min) to allow all install actions to take place."
}
}
variable "public_load_balancer_name" {
description = "GCP name for Global Public HTTPS Load balancer"
type = string
default = "webapp-global-load-balancer"
validation {
condition = can(regex("^[a-z][a-z0-9-]{3,63}", var.public_load_balancer_name))
error_message = "The public_load_balancer_name variable can only be a max of 63 characters beginning with a lower case letter, and consisting of lower case letters, numbers, and dash (-)."
}
}
variable "webapp_lb_health_check" {
description = "HTTPS Load balancer and healthcheck for webapps."
type = object({
check_interval_sec = optional(number)
timeout_sec = optional(number)
healthy_threshold = optional(number)
unhealthy_threshold = optional(number)
request_path = optional(string)
port = optional(number)
host = optional(string)
logging = optional(bool)
})
default = {
check_interval_sec = 30
timeout_sec = 10
healthy_threshold = 2
unhealthy_threshold = 3
request_path = "/api/__healthcheck"
port = 443
}
}
## Database instance variables
variable "database_vm_instance_config" {
description = "Database Compute instance configuration settings"
type = object({
machine_type = string
disk_size_gb = number
instance_role = string
name = optional(string)
name_prefix = optional(string)
disk_auto_delete = optional(bool)
description = optional(string)
disk_type = optional(string)
})
}
## Agent instance variables
variable "agent_vm_instance_config" {
description = "Agent Compute instance configuration settings"
type = object({
machine_type = string
disk_size_gb = number
instance_role = string
name = optional(string)
name_prefix = optional(string)
disk_auto_delete = optional(bool)
description = optional(string)
disk_type = optional(string)
})
}
variable "number_of_agents_per_region" {
description = "The number of static Kasm agents to deploy in each region. Set this to 0 to "
type = number
}
variable "enable_agent_nat_gateway" {
description = "Deploy Kasm Agent behind a NAT gateway"
type = bool
default = false
}
variable "agent_gpu_enabled" {
description = "Whether or not to automatically install GPU libraries. NOTE: This is useless unless you deploy Kasm agents using a GPU-based instance."
type = bool
default = false
}
## Connection Proxy (Guac) variables
variable "cpx_vm_instance_config" {
description = "CPX Compute instance configuration settings"
type = object({
machine_type = string
disk_size_gb = string
disk_type = string
})
validation {
condition = tonumber(var.cpx_vm_instance_config.disk_size_gb) > 30
error_message = "The cpx_vm_instance_config disk_size_gb should be larger than 30GB to support Kasm and other required services. Recommended minimum size is 50GB."
}
validation {
condition = contains(["pd-standard", "pd-ssd", "local-ssd", "pd-extreme"], var.cpx_vm_instance_config.disk_type)
error_message = "The cpx_vm_instance_config disk_type attribute can only be one of pd-standard, pd-ssd, pd-extreme, or local-ssd."
}
}
variable "cpx_named_ports" {
description = "CPX named ports for firewall and Google service connectivity"
type = list(object({
name = string
port = number
}))
default = [
{
name = "https"
port = 443
}
]
}
variable "cpx_hostname_prefix" {
description = "CPX hostname prefix to use for instance group"
type = string
default = "cpx"
validation {
condition = can(regex("^[a-z0-9-]{3,63}", var.cpx_hostname_prefix))
error_message = "The cpx_hostname_prefix variable can only be a max of 63 characters consisting of lower case letters, numbers, and dash (-)."
}
}
variable "cpx_autoscale_min_instances" {
description = "CPX Autoscale minimum number of instances"
type = number
default = 1
validation {
condition = var.cpx_autoscale_min_instances >= 1
error_message = "The cpx_autoscale_min_instances should be 1 at a minimum to create the instance group."
}
}
variable "cpx_autoscale_max_instances" {
description = "CPX Autoscale maximum number of instances"
type = number
default = 5
validation {
condition = var.cpx_autoscale_max_instances >= 2
error_message = "The cpx_autoscale_max_instances should be 3 at a minimum since that is the typical number of availability domains for a region."
}
}
variable "cpx_autoscale_cool_down_period" {
description = "Time in seconds for the autoscale group to wait before evaluating the health of the webapp"
type = number
default = 600
validation {
condition = var.cpx_autoscale_cool_down_period >= 300
error_message = "The cpx_autoscale_cool_down_periodc should be greater than 300 to allow webapps to be fully available before being evaluated with health checks. Kasm recommends around 600 or more."
}
}
variable "cpx_autoscale_scale_out_cpu" {
description = "CPX Autoscale CPU percent to scale up webapps"
type = list(object({
target = number
predictive_method = optional(string, "NONE")
}))
default = [{
target = 0.6
}]
validation {
condition = alltrue([for value in var.cpx_autoscale_scale_out_cpu : value.target >= 0.5])
error_message = "The cpx_autoscale_scale_out_cpu target attribute should be 0.5 at a minimum to prevent GCP from scaling webapps too aggressively."
}
validation {
condition = alltrue([for value in var.cpx_autoscale_scale_out_cpu : contains(["NONE", "OPTIMIZE_AVAILABILITY"], value.predictive_method)])
error_message = "The cpx_autoscale_scale_out_cpu predictive_method attribute can only be one of NONE or OPTIMIZE_AVAILABILITY."
}
}
variable "cpx_autoscale_scale_in_settings" {
description = "CPX Autoscale scale-in settings"
type = object({
fixed_replicas = number
time_window_sec = number
percent_replicas = optional(number, null)
})
default = {
fixed_replicas = 1
time_window_sec = 600
}
validation {
condition = var.cpx_autoscale_scale_in_settings.fixed_replicas >= 1
error_message = "The cpx_instance_group_scale_settings fixed_replicas should be 1 or more."
}
validation {
condition = var.cpx_autoscale_scale_in_settings.time_window_sec >= 300
error_message = "The cpx_instance_group_scale_settings time_window_sec should be greater than 300 to allow webapps to be fully available before being evaluated with health checks. Kasm recommends around 600 or more."
}
}
variable "cpx_instance_update_policy" {
description = "The CPX Instance group rolling update policy"
type = list(object({
instance_redistribution_type = string
min_ready_sec = number
replacement_method = string
minimal_action = string
type = string
max_surge_fixed = optional(number, null)
max_surge_percent = optional(number, null) # Can only use if you run 10 or more instances
max_unavailable_fixed = optional(number, null)
max_unavailable_percent = optional(number, null) # Can only use if you run 10 or more instances
}))
default = [{
minimal_action = "REFRESH"
type = "PROACTIVE" # Update automatically
max_surge_fixed = 3 # Max new instances to startup
max_unavailable_fixed = 0 # Max instances to take offline before new ones available
instance_redistribution_type = "PROACTIVE" # Automatically distribute nodes across availability zones
replacement_method = "SUBSTITUTE" # Start new ones before taking any offline
min_ready_sec = 600 # Wait time before instance is ready
}]
validation {
condition = alltrue([for value in var.cpx_instance_update_policy : contains(["PROACTIVE", "OPPORTUNISTIC"], value.type)])
error_message = "The cpx_instance_update_policy instance_redistribution_type setting only accepts PROACTIVE or OPPORTUNISTIC."
}
validation {
condition = alltrue([for value in var.cpx_instance_update_policy : contains(["PROACTIVE", "OPPORTUNISTIC"], value.instance_redistribution_type)])
error_message = "The cpx_instance_update_policy instance_redistribution_type setting only accepts PROACTIVE or OPPORTUNISTIC."
}
validation {
condition = alltrue([for value in var.cpx_instance_update_policy : contains(["RECREATE", "SUBSTITUTE"], value.replacement_method)])
error_message = "The cpx_instance_update_policy replacement_method setting only accepts RECREATE or SUBSTITUTE."
}
validation {
condition = alltrue([for value in var.cpx_instance_update_policy : value.min_ready_sec >= 300])
error_message = "The cpx_instance_update_policy min_ready_sec should be greater than or equal to 300 seconds (5 min). Ideally, this should be around 600 seconds (10 min) to allow all install actions to take place."
}
}
## DNS variables
variable "create_public_dns_zone" {
description = "Set to true if you wish to create a public DNS zone for this Kasm instance. If not, the public_dns_friendly_name should belong to an existing DNS zone."
type = bool
default = true
}
variable "public_dns_friendly_name" {
description = "Public DNS Zone resource name. If not creating a new DNS Zone, make sure the desired DNS zone already exists."
type = string
validation {
condition = can(regex("^[a-z][a-z0-9-]{3,63}", var.public_dns_friendly_name))
error_message = "The public_dns_friendly_name variable can only be a max of 63 characters beginning with a lower case letter, and consisting of lower case letters, numbers, and dash (-)."
}
}
variable "private_dns_friendly_name" {
description = "Private DNS Zone resource name"
type = string
validation {
condition = can(regex("^[a-z][a-z0-9-]{3,63}", var.private_dns_friendly_name))
error_message = "The private_dns_friendly_name variable can only be a max of 63 characters beginning with a lower case letter, and consisting of lower case letters, numbers, and dash (-)."
}
}
## Kasm default image source
variable "kasm_source_image" {
description = "The source VM Image information to use for deploying Kasm. Recommended to use Ubuntu 20.04 Minimal. You can either explicitly define the source image to use, or the image project and family so that Terraform always chooses the latest."
type = object({
source_image = optional(string, null)
project = optional(string, null)
family = optional(string, null)
})
default = {
project = "ubuntu-os-cloud"
family = "ubuntu-minimal-2004-lts"
}
}
## GCP Service account used autoscaling and instance updates
variable "compute_service_account" {
description = "Compute service account to use for CPX autoscaling"
type = object({
email = optional(string)
scopes = list(string)
})
default = {
email = ""
scopes = ["cloud-platform"]
}
}
## Additional install options
variable "additional_kasm_install_options" {
description = "Additional global Kasm install options. Refer to the install.sh file in the Kasm installer for additional details."
type = list(string)
default = ["-O"]
}
variable "additional_webapp_install_options" {
description = "Additional global Kasm install options. Refer to the install.sh file in the Kasm installer for additional details."
type = list(string)
default = []
}
variable "additional_database_install_options" {
description = "Additional global Kasm install options. Refer to the install.sh file in the Kasm installer for additional details."
type = list(string)
default = []
}
variable "additional_agent_install_options" {
description = "Additional global Kasm install options. Refer to the install.sh file in the Kasm installer for additional details."
type = list(string)
default = []
}
variable "additional_cpx_install_options" {
description = "Additional global Kasm install options. Refer to the install.sh file in the Kasm installer for additional details."
type = list(string)
default = []
}
## Additional Kasm features
variable "deploy_connection_proxy" {
description = "Deploy Kasm Guacamole Server for RDP/SSH access to physical servers"
type = bool
default = false
}
variable "deploy_windows_hosts" {
description = "Create a subnet and Firewall rules for Windows hosts. These hosts must be deployed manually, or you'll need to add your own compute entry for Windows hosts."
type = bool
default = false
}
## Kasm Autoscale Service Account
variable "create_kasm_autoscale_service_account" {
description = "Create a GCP service account capable of managing Kasm Cloud Autoscaling for GCP agents"
type = bool
default = false
}
variable "service_account_name" {
description = "Account name to use for Kasm Autoscaling service account"
type = string
default = ""
}
## Firewall rules
variable "kasm_firewall_security_tags" {
description = "Firewall tags to use for Kasm CPX firewall rules"
type = object({
webapp = list(string)
database = list(string)
agent = list(string)
cpx = optional(list(string), [])
windows = optional(list(string), [])
})
default = {
webapp = ["webapp"]
database = ["database"]
agent = ["kasm-agent"]
cpx = ["kasm-cpx"]
windows = ["kasm-windows"]
}
}
variable "custom_firewall_rules" {
description = "Additional, custom firewall rules"
type = list(object({
name = string
description = optional(string, null)
direction = optional(string, null)
priority = optional(number, null)
ranges = optional(list(string), null)
source_tags = optional(list(string), null)
source_service_accounts = optional(list(string), null)
target_tags = optional(list(string), null)
target_service_accounts = optional(list(string), null)
allow = optional(list(object({
protocol = string
ports = optional(list(string))
})), null)
deny = optional(list(object({
protocol = string
ports = optional(list(string))
})), null)
log_config = optional(object({
metadata = string
}), null)
}))
default = []
}
## Add any required custom routes to match your environment
variable "custom_kasm_routes" {
description = "Custom routes to add to VPC"
type = list(object({
name = string
destination_range = string
description = optional(string, null)
priority = optional(number, null)
next_hop_internet = optional(bool, false)
next_hop_ip = optional(string, null)
next_hop_instance = optional(string, null)
next_hop_instance_zone = optional(string, null)
next_hop_vpn_tunnel = optional(string, null)
next_hop_ilb = optional(string, null)
tags = optional(list(string), [])
}))
default = []
}
## Default labels to apply to all resources
variable "resource_labels" {
description = "Default tags to add to Terraform-deployed Kasm services"
type = map(any)
default = null
}