Merge branch 'ENG-233---Public-Terraform-1.14' into 'develop'

Resolve ENG-233 "Public terraform 1.14"

Closes ENG-233

See merge request kasm-technologies/internal/terraform!13
This commit is contained in:
Justin Travis 2024-02-26 18:23:50 +00:00
commit d91303ab6b
123 changed files with 4912 additions and 1741 deletions

63
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,63 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: mixed-line-ending
- id: trailing-whitespace
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.81.0
hooks:
- id: tfupdate
name: Autoupdate Terraform versions
args:
- --args=terraform
- --args=--version "~> 1.0"
- id: tfupdate
name: Autoupdate AWS version
args:
- --args=provider aws
- --args=--version "~> 5.0"
- id: tfupdate
name: Autoupdate OCI version
args:
- --args=provider oci
- --args=--version "~> 5.0"
- id: tfupdate
name: Autoupdate DigitalOcean version
args:
- --args=provider digitalocean
- --args=--version "~> 2.0"
- id: tfupdate
name: Autoupdate Acme version
args:
- --args=provider acme
- --args=--version "~> 2.0"
- id: tfupdate
name: Autoupdate TLS version
args:
- --args=provider tls
- --args=--version "~> 4.0"
- id: terraform_fmt
- id: terraform_tflint
args:
- '--args=--only=terraform_deprecated_interpolation'
- '--args=--only=terraform_deprecated_index'
- '--args=--only=terraform_unused_declarations'
- '--args=--only=terraform_comment_syntax'
- '--args=--only=terraform_documented_outputs'
- '--args=--only=terraform_documented_variables'
- '--args=--only=terraform_typed_variables'
- '--args=--only=terraform_module_pinned_source'
- '--args=--only=terraform_required_version'
- '--args=--only=terraform_required_providers'
- '--args=--minimum-failure-severity=error'
- --args=--fix
- id: terraform_validate
args:
- --tf-init-args=-upgrade
- --hook-config=--retry-once-with-cleanup=true
- id: terraform_docs
args:
- --hook-config=--path-to-file=README.md
- --hook-config=--add-to-existing-file=true
- --hook-config=--create-file-if-not-exist=true

View file

@ -12,11 +12,11 @@ It is expected that administrators will configure the
traffic does not always traverse the **Primary Region** and instead flows directly to the Agent in whichever region it
is deployed.
![Diagram][Image_Diagram]
[Image_Diagram]: https://f.hubspotusercontent30.net/hubfs/5856039/terraform/diagrams/aws-multi-region-int-gw.png "Diagram"
[Image_Diagram]: https://5856039.fs1.hubspotusercontent-na1.net/hubfs/5856039/terraform/diagrams/aws-multi-region-new.jpg "Diagram"
> ***NOTE:*** This deployment has been tested and validated with both [Terraform](https://www.terraform.io/) and [OpenTofu](https://opentofu.org/)
# Pre-Configuration
Consider creating a special sub account for the Kasm deployment.
@ -35,60 +35,106 @@ Create a user via the IAM console that will be used for the terraform deployment
1. Initialize the project
terraform init
terraform init
2. Open `settings.tfvars` and update the variable values. The variable definitions, descriptions, and validation expectations can be found in the `variables.tf` file.
2. Open `terraform.tfvars` and update the variable values. The variable definitions, descriptions, and validation expectations can be found in the `variables.tf` file.
> ***NOTE:*** This document assumes you are using a separate file named `secrets.tfvars` for the AWS credentials generated in the [AWS API Keys](#aws-api-keys) section above. The .gitignore file in this repository will ignore any files named `secrets.tfvars` since they are expected to have sensitive values in them. This will prevent you from accidentally committing them to source control.
> ***NOTE:*** This document assumes you are using a separate file named `secrets.tfvars` for the AWS credentials generated in the [AWS API Keys](#aws-api-keys) section above. The .gitignore file in this repository will ignore any files named `secrets.tfvars` since they are expected to have sensitive values in them. This will prevent you from accidentally committing them to source control. If you would rather use Environment variables or some other AWS credential method in lieu of the `secrets.tfvars` file, check out the [AWS Terraform provider documentation](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#environment-variables) for more information about configuring your environment.
3. If you are deploying more than 2 regions, you will need to modify the `provider.tf` and the `deployment.tf` files. There are commented sections in both files indicating how to deploy additional regions.
3. If you are deploying more than 2 regions, you will need to modify the `provider.tf`, `deployment.tf`, and `outputs.tf` files. There are commented sections in both files indicating how to deploy additional regions.
3. Verify the configuration
terraform plan -var-file settings.tfvars -var-file secrets.tfvars
terraform plan -var-file secrets.tfvars
4. Deploy
terraform apply -var-file settings.tfvars -var-file secrets.tfvars
terraform apply -var-file secrets.tfvars
5. Login to the Deployment as an Admin via the domain defined e.g `https://kasm.contoso.com`
5. Login to the Deployment as an Admin via the domain defined; e.g., `https://kasm.contoso.com`
6. Navigate to the Agents tab, and enable each Agent after it checks in. (May take a few minutes)
6. Navigate to the `Infrastructure > Zones` section and update the following values according to output values from this deployment.
- Upstream Auth Address
- Proxy Hostname
7. Navigate to the `Infrastructure > Agents` section and enable each Agent after it checks in. (May take a few minutes)
# AWS Terraform Variable definitions
8. Now you are ready to add Workspaces via the registry and start using Kasm!
| Variable | Description | Variable type | Example |
|:--------:|-------------|---------------|---------|
| `aws_access_key` | The AWS access key used for deployment. | String | `"AKIAJSIE27KKMHXI3BJQ"` |
| `aws_secret_key` | The AWS secret key used for deployment. | String | `"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"` |
| `aws_primary_region` | The AWS Region to deploy all Kasm Management resources. | String | `"us-east-1"` |
| `project_name` | The name of the deployment (e.g dev, staging). A short single word of up to 15 characters. | String | `"kasm"` |
| `aws_domain_name` | The Route53 Zone used for the dns entries. This must already exist in the AWS account. (e.g dev.kasm.contoso.com). The deployment will be accessed via this zone name via https. | String | `"kasm.contoso.com"` |
| `kasm_zone_name` | A name given to the kasm deployment Zone. | String | `"default"` |
| `primary_vpc_subnet_cidr` | The subnet CIDR to use for the Primary region's VPC. | String | `"10.0.0.0/16"` |
| `aws_key_pair` | The name of an aws keypair to use. | String | `"kasm_ssh_key"` |
| `primary_region_ec2_ami_id` | The AMI used for the EC2 nodes in the Primary (Management) region. Recommended Ubuntu 20.04 LTS. | String | `"ami-09cd747c78a9add63"` |
| `swap_size` | The amount of swap (in MB) to configure inside the Kasm servers. | Number | `2048` |
| `webapp_instance_type` | The instance type for the Kasm WebApps. | String | `"t3.small"` |
| `webapp_hdd_size_gb` | The HDD size for the WebApp EC2s in GB. | Number | `40` |
| `db_instance_type` | The instance type for the Kasm Database. | String | `"t3.medium"` |
| `db_hdd_size_gb` | The HDD size for the DB EC2 in GB. | Number | `40` |
| `agent_instance_type` | The instance type for the Kasm Agents in the Primary region. | String | `"t3.medium"` |
| `agent_hdd_size_gb` | The HDD size for the Agent EC2s in GB. | Number | `120` |
| `num_webapps` | The number of WebApp role servers to create in this deployment. Acceptable ranges from 1-3. | Number | `2` |
| `num_agents` | The number of static Kasm Agents to create in the primary region. Acceptable ranges from 0-100. | Number | `2` |
| `allow_ssh_cidrs` | A list of subnets in CIDR notation allowed to SSH into your kasm servers (use `["0.0.0.0/0]"` to allow SSH from any IP). | List(String) | `["1.1.1.1/32","172.217.22.14/32"]` |
| `web_access_cidrs` | A list of subnets in CIDR notation allowed Web access to your kasm servers (use `["0.0.0.0/0]"` to allow HTTP/HTTPS from any IP). | List(String) | `["0.0.0.0/0"]` |
| `secondary_regions_settings` | A map of AWS environment settings for secondary regions. The Primary region is considered "region1", thus all secondary regions should be labeled "region2", "region3", etc. Refer to the commented settings in the `secondary_regions_settings` variable in the `settings.tf` for an example. | Map(any) | <pre>{<br/>&nbsp;&nbsp;region2 = {<br/>&nbsp;&nbsp;&nbsp;&nbsp;agent_region = "eu-central-1"<br/>&nbsp;&nbsp;&nbsp;&nbsp;agent_ec2_ami_id = "ami-0e067cc8a2b58de59"<br/>&nbsp;&nbsp;&nbsp;&nbsp;agent_instance_type = "t3.medium"<br/>&nbsp;&nbsp;&nbsp;&nbsp;num_agents = 2<br/>&nbsp;&nbsp;&nbsp;&nbsp;agent_vpc_cidr = "10.1.0.0/16"<br/>&nbsp;&nbsp;}<br/>}</pre>
| `database_password` | The Kasm PostgreSQL database password. String from 12-30 characters in length with no special characters. | String | `"1qaz2wsx3EDC4RFV"` |
| `redis_password` | The Kasm Redis password. String from 12-30 characters in length with no special characters. | String | `"1qaz2wsx3EDC4RFV"` |
| `admin_password` | The Kasm Administrative user login password. String from 12-30 characters in length with no special characters. | String | `"1qaz2wsx3EDC4RFV"` |
| `user_password` | A Kasm standard (non-administrator) user password. String from 12-30 characters in length with no special characters. | String | `"1qaz2wsx3EDC4RFV"` |
| `manager_token` | The manager token value used by Kasm agents to authenticate to the Kasm WebApps. String from 12-30 characters in length with no special characters. | String | `"1qaz2wsx3EDC4RFV"` |
| `kasm_build` | The download URL for the desired Kasm Workspaces version. | String | `"https://kasm-static-content.s3.amazonaws.com/kasm_release_1.13.0.002947.tar.gz"` |
| `aws_default_tags` | A Map of all tags you wish to apply to all TF created resources in this deployment. | Map(Any) | <pre>{<br/>&nbsp;&nbsp;Service_name = "Kasm Workspaces"<br/>&nbsp;&nbsp;Kasm_version = "1.12"<br/>}</pre> |
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 5.0 |
## Providers
No providers.
## Modules
| Name | Source | Version |
|------|--------|---------|
| <a name="module_primary_region"></a> [primary\_region](#module\_primary\_region) | ./primary | n/a |
| <a name="module_primary_region_webapps_and_agents"></a> [primary\_region\_webapps\_and\_agents](#module\_primary\_region\_webapps\_and\_agents) | ./webapps | n/a |
| <a name="module_region2_agents"></a> [region2\_agents](#module\_region2\_agents) | ./agents | n/a |
| <a name="module_region2_webapps"></a> [region2\_webapps](#module\_region2\_webapps) | ./webapps | n/a |
## Resources
No resources.
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_admin_password"></a> [admin\_password](#input\_admin\_password) | The administrative user password. No special characters | `string` | n/a | yes |
| <a name="input_agent_hdd_size_gb"></a> [agent\_hdd\_size\_gb](#input\_agent\_hdd\_size\_gb) | The HDD size in GB to configure for the Kasm Agent instances | `number` | n/a | yes |
| <a name="input_agent_instance_type"></a> [agent\_instance\_type](#input\_agent\_instance\_type) | The instance type for the Agents | `string` | n/a | yes |
| <a name="input_aws_access_key"></a> [aws\_access\_key](#input\_aws\_access\_key) | The AWS access key used for deployment | `string` | n/a | yes |
| <a name="input_aws_default_tags"></a> [aws\_default\_tags](#input\_aws\_default\_tags) | Default tags to apply to all AWS resources for this deployment | `map(any)` | <pre>{<br> "Kasm_version": "1.14",<br> "Service_name": "Kasm Workspaces"<br>}</pre> | no |
| <a name="input_aws_domain_name"></a> [aws\_domain\_name](#input\_aws\_domain\_name) | The Route53 Zone used for the dns entries. This must already exist in the AWS account. (e.g dev.kasm.contoso.com). The deployment will be accessed via this zone name via https | `string` | n/a | yes |
| <a name="input_aws_key_pair"></a> [aws\_key\_pair](#input\_aws\_key\_pair) | The name of an aws keypair to use. | `string` | n/a | yes |
| <a name="input_aws_primary_region"></a> [aws\_primary\_region](#input\_aws\_primary\_region) | The AWS Region used for deployment | `string` | `"us-east-1"` | no |
| <a name="input_aws_secret_key"></a> [aws\_secret\_key](#input\_aws\_secret\_key) | The AWS secret key used for deployment | `string` | n/a | yes |
| <a name="input_aws_ssm_iam_role_name"></a> [aws\_ssm\_iam\_role\_name](#input\_aws\_ssm\_iam\_role\_name) | The name of the SSM EC2 role to associate with Kasm VMs for SSH access | `string` | `""` | no |
| <a name="input_aws_ssm_instance_profile_name"></a> [aws\_ssm\_instance\_profile\_name](#input\_aws\_ssm\_instance\_profile\_name) | The name of the SSM EC2 Instance Profile to associate with Kasm VMs for SSH access | `string` | `""` | no |
| <a name="input_cpx_hdd_size_gb"></a> [cpx\_hdd\_size\_gb](#input\_cpx\_hdd\_size\_gb) | The HDD size in GB to configure for the Kasm Guac RDP instances | `number` | n/a | yes |
| <a name="input_cpx_instance_type"></a> [cpx\_instance\_type](#input\_cpx\_instance\_type) | The instance type for the Guac RDP nodes | `string` | n/a | yes |
| <a name="input_create_aws_ssm_iam_role"></a> [create\_aws\_ssm\_iam\_role](#input\_create\_aws\_ssm\_iam\_role) | Create an AWS SSM IAM role to attach to VMs for SSH/console access to VMs. | `bool` | `false` | no |
| <a name="input_database_password"></a> [database\_password](#input\_database\_password) | The password for the database. No special characters | `string` | n/a | yes |
| <a name="input_db_hdd_size_gb"></a> [db\_hdd\_size\_gb](#input\_db\_hdd\_size\_gb) | The HDD size in GB to configure for the Kasm Database instances | `number` | n/a | yes |
| <a name="input_db_instance_type"></a> [db\_instance\_type](#input\_db\_instance\_type) | The instance type for the Database | `string` | n/a | yes |
| <a name="input_kasm_build"></a> [kasm\_build](#input\_kasm\_build) | Download URL for Kasm Workspaces | `string` | n/a | yes |
| <a name="input_manager_token"></a> [manager\_token](#input\_manager\_token) | The manager token value for Agents to authenticate to webapps. No special characters | `string` | n/a | yes |
| <a name="input_num_agents"></a> [num\_agents](#input\_num\_agents) | The number of Agent Role Servers to create in the deployment | `number` | `2` | no |
| <a name="input_num_cpx_nodes"></a> [num\_cpx\_nodes](#input\_num\_cpx\_nodes) | The number of RDP Conection Proxy Role Servers to create in the deployment. Set this to zero (0) and this Terraform will not deploy ANY Connection Proxy or Windows resoures like subnets, security groups, etc. | `number` | n/a | yes |
| <a name="input_num_proxy_nodes"></a> [num\_proxy\_nodes](#input\_num\_proxy\_nodes) | The number of Dedicated Proxy nodes to create in the deployment | `number` | n/a | yes |
| <a name="input_num_webapps"></a> [num\_webapps](#input\_num\_webapps) | The number of WebApp role servers to create in the deployment | `number` | `2` | no |
| <a name="input_primary_region_ec2_ami_id"></a> [primary\_region\_ec2\_ami\_id](#input\_primary\_region\_ec2\_ami\_id) | AMI Id of Kasm EC2 image in the primary region. Recommended AMI OS Version is Ubuntu 20.04 LTS. | `string` | n/a | yes |
| <a name="input_primary_vpc_subnet_cidr"></a> [primary\_vpc\_subnet\_cidr](#input\_primary\_vpc\_subnet\_cidr) | The subnet CIDR to use for the VPC | `string` | `"10.0.0.0/16"` | no |
| <a name="input_project_name"></a> [project\_name](#input\_project\_name) | The name of the deployment (e.g dev, staging). A short single word | `string` | n/a | yes |
| <a name="input_proxy_hdd_size_gb"></a> [proxy\_hdd\_size\_gb](#input\_proxy\_hdd\_size\_gb) | The HDD size in GB to configure for the Kasm dedicated proxy instances | `number` | n/a | yes |
| <a name="input_proxy_instance_type"></a> [proxy\_instance\_type](#input\_proxy\_instance\_type) | The instance type for the dedicated proxy node | `string` | `""` | no |
| <a name="input_redis_password"></a> [redis\_password](#input\_redis\_password) | The password for the Redis server. No special characters | `string` | n/a | yes |
| <a name="input_secondary_regions_settings"></a> [secondary\_regions\_settings](#input\_secondary\_regions\_settings) | Map of Kasm settings for secondary regions | <pre>map(object({<br> agent_region = string<br> agent_vpc_cidr = string<br> ec2_ami_id = string<br> })<br> )</pre> | n/a | yes |
| <a name="input_service_registration_token"></a> [service\_registration\_token](#input\_service\_registration\_token) | The service registration token value for cpx RDP servers to authenticate to webapps. No special characters | `string` | n/a | yes |
| <a name="input_ssh_access_cidrs"></a> [ssh\_access\_cidrs](#input\_ssh\_access\_cidrs) | CIDR notation of the bastion host allowed to SSH in to the machines | `list(string)` | <pre>[<br> "0.0.0.0/0"<br>]</pre> | no |
| <a name="input_swap_size"></a> [swap\_size](#input\_swap\_size) | The amount of swap (in MB) to configure inside the compute instances | `number` | n/a | yes |
| <a name="input_user_password"></a> [user\_password](#input\_user\_password) | The standard (non administrator) user password. No special characters | `string` | n/a | yes |
| <a name="input_web_access_cidrs"></a> [web\_access\_cidrs](#input\_web\_access\_cidrs) | CIDR notation of the bastion host allowed to SSH in to the machines | `list(string)` | <pre>[<br> "0.0.0.0/0"<br>]</pre> | no |
| <a name="input_webapp_hdd_size_gb"></a> [webapp\_hdd\_size\_gb](#input\_webapp\_hdd\_size\_gb) | The HDD size in GB to configure for the Kasm WebApp instances | `number` | n/a | yes |
| <a name="input_webapp_instance_type"></a> [webapp\_instance\_type](#input\_webapp\_instance\_type) | The instance type for the webapps | `string` | `""` | no |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_region1_zone_settings"></a> [region1\_zone\_settings](#output\_region1\_zone\_settings) | Upstream Auth and Proxy settings to apply to Kasm Primary Region Zone configuration |
| <a name="output_region2_zone_settings"></a> [region2\_zone\_settings](#output\_region2\_zone\_settings) | Upstream Auth and Proxy settings to apply to Kasm Agent Region 2 Zone configuration |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
# Detailed Terraform Deployment Diagram

View file

@ -0,0 +1,109 @@
# agents
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 5.0 |
## Providers
| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | 5.36.0 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [aws_acm_certificate.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate) | resource |
| [aws_acm_certificate_validation.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation) | resource |
| [aws_eip.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource |
| [aws_instance.agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource |
| [aws_instance.cpx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource |
| [aws_instance.proxy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource |
| [aws_internet_gateway.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/internet_gateway) | resource |
| [aws_lb.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb) | resource |
| [aws_lb_listener.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener) | resource |
| [aws_lb_target_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_target_group) | resource |
| [aws_lb_target_group_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_target_group_attachment) | resource |
| [aws_nat_gateway.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/nat_gateway) | resource |
| [aws_route53_record.alb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_route53_record.certificate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_route_table.internet_gateway](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource |
| [aws_route_table.nat_gateway](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource |
| [aws_route_table_association.agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_route_table_association.alb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_route_table_association.cpx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_route_table_association.proxy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_route_table_association.windows](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_security_group.agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.cpx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.proxy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.public_lb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.windows](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group_rule.agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.agent_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.cpx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.cpx_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.proxy_agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.proxy_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.proxy_public_lb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.public_lb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.public_lb_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.windows_cpx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.windows_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.windows_webapp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_subnet.agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_subnet.alb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_subnet.cpx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_subnet.proxy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_subnet.windows](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_vpc.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc) | resource |
| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source |
| [aws_route53_zone.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route53_zone) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_agent_hdd_size_gb"></a> [agent\_hdd\_size\_gb](#input\_agent\_hdd\_size\_gb) | The HDD size for agents | `number` | n/a | yes |
| <a name="input_agent_instance_type"></a> [agent\_instance\_type](#input\_agent\_instance\_type) | The instance type for the agents | `string` | n/a | yes |
| <a name="input_agent_security_rules"></a> [agent\_security\_rules](#input\_agent\_security\_rules) | A map of objects of security rules to apply to the Kasm WebApp server | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> }))</pre> | <pre>{<br> "https": {<br> "from_port": 443,<br> "protocol": "tcp",<br> "to_port": 443<br> }<br>}</pre> | no |
| <a name="input_agent_vpc_cidr"></a> [agent\_vpc\_cidr](#input\_agent\_vpc\_cidr) | Subnet CIDR range for Agent VPC | `string` | n/a | yes |
| <a name="input_anywhere"></a> [anywhere](#input\_anywhere) | Anywhere subnet for routing and load ingress from all IPs | `string` | `"0.0.0.0/0"` | no |
| <a name="input_aws_domain_name"></a> [aws\_domain\_name](#input\_aws\_domain\_name) | The Route53 Zone used for the dns entries. This must already exist in the AWS account. (e.g dev.kasm.contoso.com). The deployment will be accessed via this zone name via https | `string` | n/a | yes |
| <a name="input_aws_key_pair"></a> [aws\_key\_pair](#input\_aws\_key\_pair) | The name of an aws keypair to use. | `string` | n/a | yes |
| <a name="input_aws_region"></a> [aws\_region](#input\_aws\_region) | The AWS region for the deployment. (e.g us-east-1) | `string` | n/a | yes |
| <a name="input_aws_ssm_instance_profile_name"></a> [aws\_ssm\_instance\_profile\_name](#input\_aws\_ssm\_instance\_profile\_name) | The name of the SSM EC2 Instance Profile to associate with Kasm VMs for SSH access | `string` | `""` | no |
| <a name="input_cpx_hdd_size_gb"></a> [cpx\_hdd\_size\_gb](#input\_cpx\_hdd\_size\_gb) | The HDD size for Kasm Guac RDP nodes | `number` | n/a | yes |
| <a name="input_cpx_instance_type"></a> [cpx\_instance\_type](#input\_cpx\_instance\_type) | The instance type for the cpx RDP nodes | `string` | n/a | yes |
| <a name="input_cpx_security_rules"></a> [cpx\_security\_rules](#input\_cpx\_security\_rules) | A map of objects of security rules to apply to the Kasm Connection Proxy server | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> }))</pre> | <pre>{<br> "https": {<br> "from_port": 443,<br> "protocol": "tcp",<br> "to_port": 443<br> }<br>}</pre> | no |
| <a name="input_default_egress"></a> [default\_egress](#input\_default\_egress) | Default egress security rule for all security groups | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> cidr_subnets = list(string)<br> }))</pre> | <pre>{<br> "all": {<br> "cidr_subnets": [<br> "0.0.0.0/0"<br> ],<br> "from_port": 0,<br> "protocol": "-1",<br> "to_port": 0<br> }<br>}</pre> | no |
| <a name="input_ec2_ami"></a> [ec2\_ami](#input\_ec2\_ami) | The AMI used for the EC2 nodes. Recommended Ubuntu 20.04 LTS. | `string` | n/a | yes |
| <a name="input_kasm_build"></a> [kasm\_build](#input\_kasm\_build) | The URL for the Kasm Workspaces build | `string` | n/a | yes |
| <a name="input_management_region_nat_gateway"></a> [management\_region\_nat\_gateway](#input\_management\_region\_nat\_gateway) | A list Kasm management region NAT gateways to allow Webapps ingress on 4902 to Kasm Windows agent | `string` | n/a | yes |
| <a name="input_manager_token"></a> [manager\_token](#input\_manager\_token) | The password for the database. No special characters | `string` | n/a | yes |
| <a name="input_num_agents"></a> [num\_agents](#input\_num\_agents) | The number of Agent Role Servers to create in the deployment | `number` | n/a | yes |
| <a name="input_num_cpx_nodes"></a> [num\_cpx\_nodes](#input\_num\_cpx\_nodes) | The number of cpx Role Servers to create in the deployment | `number` | n/a | yes |
| <a name="input_num_proxy_nodes"></a> [num\_proxy\_nodes](#input\_num\_proxy\_nodes) | The number of Dedicated Proxy nodes to create in the deployment | `number` | `2` | no |
| <a name="input_project_name"></a> [project\_name](#input\_project\_name) | The name of the deployment (e.g dev, staging). A short single word | `string` | n/a | yes |
| <a name="input_proxy_hdd_size_gb"></a> [proxy\_hdd\_size\_gb](#input\_proxy\_hdd\_size\_gb) | The HDD size for Dedicated Proxy nodes | `number` | n/a | yes |
| <a name="input_proxy_instance_type"></a> [proxy\_instance\_type](#input\_proxy\_instance\_type) | The instance type for the dedicated proxy nodes | `string` | n/a | yes |
| <a name="input_proxy_security_rules"></a> [proxy\_security\_rules](#input\_proxy\_security\_rules) | A map of objects of security rules to apply to the Kasm WebApp server | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> }))</pre> | <pre>{<br> "https": {<br> "from_port": 443,<br> "protocol": "tcp",<br> "to_port": 443<br> }<br>}</pre> | no |
| <a name="input_public_lb_security_rules"></a> [public\_lb\_security\_rules](#input\_public\_lb\_security\_rules) | A map of objects of security rules to apply to the Public ALB | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> }))</pre> | <pre>{<br> "http": {<br> "from_port": 80,<br> "protocol": "tcp",<br> "to_port": 80<br> },<br> "https": {<br> "from_port": 443,<br> "protocol": "tcp",<br> "to_port": 443<br> }<br>}</pre> | no |
| <a name="input_service_registration_token"></a> [service\_registration\_token](#input\_service\_registration\_token) | The service registration token value for cpx RDP servers to authenticate to webapps. No special characters | `string` | n/a | yes |
| <a name="input_swap_size"></a> [swap\_size](#input\_swap\_size) | The amount of swap (in MB) to configure inside the compute instances | `number` | n/a | yes |
| <a name="input_web_access_cidrs"></a> [web\_access\_cidrs](#input\_web\_access\_cidrs) | List of Networks in CIDR notation for IPs allowed to access the Kasm Web interface | `list(string)` | <pre>[<br> "0.0.0.0/0"<br>]</pre> | no |
| <a name="input_windows_security_rules"></a> [windows\_security\_rules](#input\_windows\_security\_rules) | A map of objects of security rules to apply to the Kasm Windows VMs | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> }))</pre> | <pre>{<br> "api": {<br> "from_port": 4902,<br> "protocol": "tcp",<br> "to_port": 4902<br> },<br> "rdp": {<br> "from_port": 3389,<br> "protocol": "tcp",<br> "to_port": 3389<br> }<br>}</pre> | no |
## Outputs
No outputs.
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

View file

@ -1,10 +1,12 @@
resource "aws_instance" "kasm-agent" {
count = var.num_agents
ami = var.ec2_ami
instance_type = var.agent_instance_type
vpc_security_group_ids = [data.aws_security_group.data-kasm_agent_sg.id]
subnet_id = data.aws_subnet.data-kasm_agent_subnet.id
key_name = var.aws_key_pair
resource "aws_instance" "agent" {
count = var.num_agents
ami = var.ec2_ami
instance_type = var.agent_instance_type
vpc_security_group_ids = [aws_security_group.agent.id]
subnet_id = aws_subnet.agent.id
key_name = var.aws_key_pair
associate_public_ip_address = true
iam_instance_profile = var.aws_ssm_instance_profile_name
root_block_device {
volume_size = var.agent_hdd_size_gb
@ -19,7 +21,14 @@ resource "aws_instance" "kasm-agent" {
}
)
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
instance_metadata_tags = null
}
tags = {
Name = "${var.project_name}-${var.zone_name}-kasm-agent"
Name = "${var.project_name}-${var.aws_region}-kasm-agent-${count.index}"
}
}

View file

@ -1,3 +0,0 @@
data "aws_availability_zones" "available" {
state = "available"
}

View file

@ -0,0 +1,32 @@
resource "aws_acm_certificate" "this" {
domain_name = var.aws_domain_name
subject_alternative_names = ["*.${var.aws_domain_name}"]
validation_method = "DNS"
lifecycle {
create_before_destroy = true
}
}
resource "aws_route53_record" "certificate" {
for_each = {
for dvo in aws_acm_certificate.this.domain_validation_options : dvo.domain_name => {
name = dvo.resource_record_name
record = dvo.resource_record_value
type = dvo.resource_record_type
}
}
name = each.value.name
type = each.value.type
records = [each.value.record]
zone_id = data.aws_route53_zone.this.id
ttl = 30
allow_overwrite = true
}
resource "aws_acm_certificate_validation" "this" {
certificate_arn = aws_acm_certificate.this.arn
validation_record_fqdns = [for record in aws_route53_record.certificate : record.fqdn]
}

View file

@ -0,0 +1,34 @@
resource "aws_instance" "cpx" {
count = var.num_cpx_nodes
ami = var.ec2_ami
instance_type = var.cpx_instance_type
vpc_security_group_ids = aws_security_group.cpx[*].id
subnet_id = aws_subnet.cpx[0].id
key_name = var.aws_key_pair
iam_instance_profile = var.aws_ssm_instance_profile_name
root_block_device {
volume_size = var.cpx_hdd_size_gb
}
user_data = templatefile("${path.module}/../userdata/cpx_bootstrap.sh",
{
kasm_build_url = var.kasm_build
swap_size = var.swap_size
manager_address = var.aws_domain_name
service_registration_token = var.service_registration_token
}
)
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
instance_metadata_tags = null
}
tags = {
Name = "${var.project_name}-${var.aws_region}-kasm-cpx-${count.index}"
}
}

View file

@ -0,0 +1,16 @@
locals {
kasm_agent_vpc_subnet_cidr_mask = split("/", var.agent_vpc_cidr)[1]
kasm_agent_subnet_cidr_calculation = (8 - (local.kasm_agent_vpc_subnet_cidr_mask - 16))
kasm_agent_subnet_cidr_size = local.kasm_agent_subnet_cidr_calculation < 3 ? 3 : local.kasm_agent_subnet_cidr_calculation
region_short_name_for_lb = join("", slice(split("-", var.aws_region), 1, 3))
}
data "aws_route53_zone" "this" {
name = var.aws_domain_name
private_zone = false
}
data "aws_availability_zones" "available" {
state = "available"
}

View file

@ -0,0 +1,52 @@
resource "aws_lb" "this" {
name = "${var.project_name}-${var.aws_region}-proxy-lb"
internal = false
load_balancer_type = "application"
security_groups = [aws_security_group.public_lb.id]
subnets = aws_subnet.alb[*].id
}
resource "aws_lb_listener" "this" {
load_balancer_arn = aws_lb.this.arn
port = "443"
protocol = "HTTPS"
certificate_arn = aws_acm_certificate.this.arn
default_action {
type = "forward"
target_group_arn = aws_lb_target_group.this.arn
}
}
resource "aws_lb_target_group" "this" {
name = "${var.project_name}-${local.region_short_name_for_lb}-tg"
port = 443
protocol = "HTTPS"
vpc_id = aws_vpc.this.id
health_check {
path = "/desktop"
matcher = 301
protocol = "HTTPS"
}
}
resource "aws_lb_target_group_attachment" "this" {
count = var.num_proxy_nodes
target_group_arn = aws_lb_target_group.this.arn
target_id = aws_instance.proxy[count.index].id
port = 443
}
resource "aws_route53_record" "alb" {
zone_id = data.aws_route53_zone.this.zone_id
name = "${local.region_short_name_for_lb}-proxy.${var.aws_domain_name}"
type = "A"
alias {
name = aws_lb.this.dns_name
zone_id = aws_lb.this.zone_id
evaluate_target_health = false
}
}

View file

@ -1,8 +1,10 @@
terraform {
required_version = "~> 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
#version = "4.56.0"
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}

View file

@ -0,0 +1,34 @@
resource "aws_instance" "proxy" {
count = var.num_proxy_nodes
ami = var.ec2_ami
instance_type = var.proxy_instance_type
vpc_security_group_ids = [aws_security_group.proxy.id]
subnet_id = aws_subnet.proxy[(count.index)].id
key_name = var.aws_key_pair
iam_instance_profile = var.aws_ssm_instance_profile_name
root_block_device {
volume_size = var.proxy_hdd_size_gb
}
user_data = templatefile("${path.module}/../userdata/proxy_bootstrap.sh",
{
kasm_build_url = var.kasm_build
swap_size = var.swap_size
manager_address = var.aws_domain_name
proxy_alb_address = "${var.aws_region}-proxy.${var.aws_domain_name}"
}
)
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
instance_metadata_tags = null
}
tags = {
Name = "${var.project_name}-${var.aws_region}-kasm-proxy-${count.index}"
}
}

View file

@ -1,21 +1,54 @@
resource "aws_route_table" "internet_access" {
vpc_id = data.aws_vpc.data-kasm_agent_vpc.id
resource "aws_route_table" "internet_gateway" {
vpc_id = aws_vpc.this.id
route {
cidr_block = var.anywhere
gateway_id = data.aws_internet_gateway.data-kasm_agent_default_ig.id
gateway_id = aws_internet_gateway.this.id
}
tags = {
Name = "${var.project_name}-kasm-agent-default-route"
Name = "${var.project_name}-kasm-internet-gateway-route"
}
}
data "aws_route_table" "data-agent_internet_gateway_route_table" {
route_table_id = aws_route_table.internet_access.id
resource "aws_route_table" "nat_gateway" {
vpc_id = aws_vpc.this.id
route {
cidr_block = var.anywhere
nat_gateway_id = aws_nat_gateway.this.id
}
tags = {
Name = "${var.project_name}-kasm-nat-gateway-route"
}
}
resource "aws_route_table_association" "agent_table_association" {
subnet_id = data.aws_subnet.data-kasm_agent_subnet.id
route_table_id = data.aws_route_table.data-agent_internet_gateway_route_table.id
resource "aws_route_table_association" "alb" {
count = 2
subnet_id = aws_subnet.alb[(count.index)].id
route_table_id = aws_route_table.internet_gateway.id
}
resource "aws_route_table_association" "proxy" {
count = var.num_proxy_nodes
subnet_id = aws_subnet.proxy[(count.index)].id
route_table_id = aws_route_table.nat_gateway.id
}
resource "aws_route_table_association" "agent" {
subnet_id = aws_subnet.agent.id
route_table_id = aws_route_table.internet_gateway.id
}
resource "aws_route_table_association" "cpx" {
count = var.num_cpx_nodes > 0 ? 1 : 0
subnet_id = aws_subnet.cpx[0].id
route_table_id = aws_route_table.nat_gateway.id
}
resource "aws_route_table_association" "windows" {
count = var.num_cpx_nodes > 0 ? 1 : 0
subnet_id = aws_subnet.windows[0].id
route_table_id = aws_route_table.internet_gateway.id
}

View file

@ -1,30 +1,197 @@
resource "aws_security_group" "kasm-agent-sg" {
name = "${var.project_name}-${var.zone_name}-kasm-agent-access"
resource "aws_security_group" "public_lb" {
name = "${var.project_name}-kasm-allow-public-lb-access"
description = "Security Group for ELB"
vpc_id = aws_vpc.this.id
tags = {
Name = "${var.project_name}-kasm-public-lb-access"
}
}
resource "aws_security_group_rule" "public_lb" {
for_each = var.public_lb_security_rules
description = "Allow Public LB ingress from ${join(",", var.web_access_cidrs)}"
security_group_id = aws_security_group.public_lb.id
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = var.web_access_cidrs
}
resource "aws_security_group_rule" "public_lb_egress" {
for_each = var.default_egress
description = "Allow Public LB egress"
security_group_id = aws_security_group.public_lb.id
type = "egress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = each.value.cidr_subnets
}
resource "aws_security_group" "proxy" {
name = "${var.project_name}-kasm-proxy"
description = "Allow access to proxy"
vpc_id = aws_vpc.this.id
tags = {
Name = "${var.project_name}-kasm-proxy-access"
}
}
resource "aws_security_group_rule" "proxy_public_lb" {
for_each = var.proxy_security_rules
security_group_id = aws_security_group.proxy.id
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
source_security_group_id = aws_security_group.public_lb.id
}
resource "aws_security_group_rule" "proxy_agent" {
for_each = var.proxy_security_rules
description = "Allow Proxy ingress from Public LB"
security_group_id = aws_security_group.proxy.id
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
source_security_group_id = aws_security_group.agent.id
}
resource "aws_security_group_rule" "proxy_egress" {
for_each = var.default_egress
description = "Allow Proxy Egress"
security_group_id = aws_security_group.proxy.id
type = "egress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = each.value.cidr_subnets
}
resource "aws_security_group" "agent" {
name = "${var.project_name}-kasm-agent-access"
description = "Allow access to agents"
vpc_id = data.aws_vpc.data-kasm_agent_vpc.id
vpc_id = aws_vpc.this.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = var.ssh_access_cidrs
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = [var.anywhere]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = [var.anywhere]
tags = {
Name = "${var.project_name}-kasm-agent-access"
}
}
data "aws_security_group" "data-kasm_agent_sg" {
id = aws_security_group.kasm-agent-sg.id
resource "aws_security_group_rule" "agent" {
for_each = var.agent_security_rules
description = "Allow Kasm Agent ingress from Proxy"
security_group_id = aws_security_group.agent.id
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
source_security_group_id = aws_security_group.proxy.id
}
resource "aws_security_group_rule" "agent_egress" {
for_each = var.default_egress
description = "Allow Agents egress"
security_group_id = aws_security_group.agent.id
type = "egress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = each.value.cidr_subnets
}
resource "aws_security_group" "cpx" {
count = var.num_cpx_nodes > 0 ? 1 : 0
name = "${var.project_name}-kasm-cpx-access"
description = "Allow access to cpx RDP nodes"
vpc_id = aws_vpc.this.id
tags = {
Name = "${var.project_name}-kasm-cpx-access"
}
}
resource "aws_security_group_rule" "cpx" {
for_each = var.num_cpx_nodes > 0 ? var.cpx_security_rules : {}
description = "Allow Kasm CPX ingress from Kasm Proxy"
security_group_id = one(aws_security_group.cpx[*].id)
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
source_security_group_id = aws_security_group.proxy.id
}
resource "aws_security_group_rule" "cpx_egress" {
for_each = var.num_cpx_nodes > 0 ? var.default_egress : {}
description = "Allow Kasm CPX egress"
security_group_id = one(aws_security_group.cpx[*].id)
type = "egress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = each.value.cidr_subnets
}
resource "aws_security_group" "windows" {
count = var.num_cpx_nodes > 0 ? 1 : 0
name = "${var.project_name}-kasm-windows-access"
description = "Allow access to Windows servers"
vpc_id = aws_vpc.this.id
tags = {
Name = "${var.project_name}-kasm-windows-access"
}
}
resource "aws_security_group_rule" "windows_cpx" {
for_each = var.num_cpx_nodes > 0 ? var.windows_security_rules : {}
description = "Allow Windows ingress from Kasm CPX"
security_group_id = one(aws_security_group.windows[*].id)
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
source_security_group_id = one(aws_security_group.cpx[*].id)
}
resource "aws_security_group_rule" "windows_webapp" {
for_each = var.num_cpx_nodes > 0 ? { for key, value in var.windows_security_rules : key => value if can(regex("(?i:api)", key)) } : {}
description = "Allow Windows ingress from Kasm WebApp"
security_group_id = one(aws_security_group.windows[*].id)
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = [var.management_region_nat_gateway]
}
resource "aws_security_group_rule" "windows_egress" {
for_each = var.num_cpx_nodes > 0 ? var.default_egress : {}
description = "Allow Windows egress"
security_group_id = one(aws_security_group.windows[*].id)
type = "egress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = each.value.cidr_subnets
}

View file

@ -1,19 +1,59 @@
locals {
kasm_agent_vpc_subnet_cidr_mask = split("/", var.agent_vpc_cidr)[1]
kasm_agent_subnet_cidr_calculation = (8 - (local.kasm_agent_vpc_subnet_cidr_mask - 16))
kasm_agent_subnet_cidr_size = local.kasm_agent_subnet_cidr_calculation < 0 ? 0 : local.kasm_agent_subnet_cidr_calculation
}
resource "aws_subnet" "kasm-agent-subnet" {
vpc_id = data.aws_vpc.data-kasm_agent_vpc.id
cidr_block = cidrsubnet(var.agent_vpc_cidr, local.kasm_agent_subnet_cidr_size, 0)
availability_zone = data.aws_availability_zones.available.names[0]
resource "aws_subnet" "alb" {
count = 2
vpc_id = aws_vpc.this.id
cidr_block = cidrsubnet(var.agent_vpc_cidr, local.kasm_agent_subnet_cidr_size, count.index)
availability_zone = data.aws_availability_zones.available.names[(count.index)]
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-${var.zone_name}-kasm-agent-subnet"
Name = "${var.project_name}-${var.aws_region}-kasm-alb-subnet"
}
}
data "aws_subnet" "data-kasm_agent_subnet" {
id = aws_subnet.kasm-agent-subnet.id
resource "aws_subnet" "proxy" {
count = var.num_proxy_nodes
vpc_id = aws_vpc.this.id
cidr_block = cidrsubnet(var.agent_vpc_cidr, local.kasm_agent_subnet_cidr_size, (count.index + 2))
availability_zone = data.aws_availability_zones.available.names[(count.index)]
tags = {
Name = "${var.project_name}-${var.aws_region}-kasm-proxy-subnet"
}
}
resource "aws_subnet" "agent" {
vpc_id = aws_vpc.this.id
cidr_block = cidrsubnet(var.agent_vpc_cidr, local.kasm_agent_subnet_cidr_size, 4)
availability_zone = data.aws_availability_zones.available.names[0]
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-${var.aws_region}-kasm-agent-subnet"
}
}
resource "aws_subnet" "cpx" {
count = var.num_cpx_nodes > 0 ? 1 : 0
vpc_id = aws_vpc.this.id
cidr_block = cidrsubnet(var.agent_vpc_cidr, local.kasm_agent_subnet_cidr_size, 5)
availability_zone = data.aws_availability_zones.available.names[0]
tags = {
Name = "${var.project_name}-${var.aws_region}-kasm-cpx-subnet"
}
}
resource "aws_subnet" "windows" {
count = var.num_cpx_nodes > 0 ? 1 : 0
vpc_id = aws_vpc.this.id
cidr_block = cidrsubnet(var.agent_vpc_cidr, local.kasm_agent_subnet_cidr_size, 6)
availability_zone = data.aws_availability_zones.available.names[0]
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-${var.aws_region}-kasm-windows-subnet"
}
}

View file

@ -33,6 +33,43 @@ variable "agent_hdd_size_gb" {
type = number
}
variable "num_cpx_nodes" {
description = "The number of cpx Role Servers to create in the deployment"
type = number
}
variable "cpx_instance_type" {
description = "The instance type for the cpx RDP nodes"
type = string
}
variable "cpx_hdd_size_gb" {
description = "The HDD size for Kasm Guac RDP nodes"
type = number
}
variable "aws_ssm_instance_profile_name" {
description = "The name of the SSM EC2 Instance Profile to associate with Kasm VMs for SSH access"
type = string
default = ""
}
variable "num_proxy_nodes" {
description = "The number of Dedicated Proxy nodes to create in the deployment"
type = number
default = 2
}
variable "proxy_instance_type" {
description = "The instance type for the dedicated proxy nodes"
type = string
}
variable "proxy_hdd_size_gb" {
description = "The HDD size for Dedicated Proxy nodes"
type = number
}
variable "aws_region" {
description = "The AWS region for the deployment. (e.g us-east-1)"
type = string
@ -43,11 +80,6 @@ variable "kasm_build" {
type = string
}
variable "zone_name" {
description = "A name given to the Kasm deployment Zone"
type = string
}
variable "aws_key_pair" {
description = "The name of an aws keypair to use."
type = string
@ -64,9 +96,15 @@ variable "manager_token" {
sensitive = true
}
variable "ssh_access_cidrs" {
description = "CIDR notation of the bastion host allowed to SSH in to the machines"
type = list(string)
variable "service_registration_token" {
description = "The service registration token value for cpx RDP servers to authenticate to webapps. No special characters"
type = string
sensitive = true
}
variable "management_region_nat_gateway" {
description = "A list Kasm management region NAT gateways to allow Webapps ingress on 4902 to Kasm Windows agent"
type = string
}
variable "anywhere" {
@ -74,3 +112,123 @@ variable "anywhere" {
type = string
default = "0.0.0.0/0"
}
variable "web_access_cidrs" {
description = "List of Networks in CIDR notation for IPs allowed to access the Kasm Web interface"
type = list(string)
default = ["0.0.0.0/0"]
}
variable "public_lb_security_rules" {
description = "A map of objects of security rules to apply to the Public ALB"
type = map(object({
from_port = number
to_port = number
protocol = string
}))
default = {
https = {
from_port = 443
to_port = 443
protocol = "tcp"
}
http = {
from_port = 80
to_port = 80
protocol = "tcp"
}
}
}
variable "proxy_security_rules" {
description = "A map of objects of security rules to apply to the Kasm WebApp server"
type = map(object({
from_port = number
to_port = number
protocol = string
}))
default = {
https = {
from_port = 443
to_port = 443
protocol = "tcp"
}
}
}
variable "cpx_security_rules" {
description = "A map of objects of security rules to apply to the Kasm Connection Proxy server"
type = map(object({
from_port = number
to_port = number
protocol = string
}))
default = {
https = {
from_port = 443
to_port = 443
protocol = "tcp"
}
}
}
variable "agent_security_rules" {
description = "A map of objects of security rules to apply to the Kasm WebApp server"
type = map(object({
from_port = number
to_port = number
protocol = string
}))
default = {
https = {
from_port = 443
to_port = 443
protocol = "tcp"
}
}
}
variable "windows_security_rules" {
description = "A map of objects of security rules to apply to the Kasm Windows VMs"
type = map(object({
from_port = number
to_port = number
protocol = string
}))
default = {
rdp = {
from_port = 3389
to_port = 3389
protocol = "tcp"
}
api = {
from_port = 4902
to_port = 4902
protocol = "tcp"
}
}
}
variable "default_egress" {
description = "Default egress security rule for all security groups"
type = map(object({
from_port = number
to_port = number
protocol = string
cidr_subnets = list(string)
}))
default = {
all = {
from_port = 0
to_port = 0
protocol = "-1"
cidr_subnets = ["0.0.0.0/0"]
}
}
}

View file

@ -1,23 +1,32 @@
resource "aws_vpc" "kasm-agent-vpc" {
resource "aws_vpc" "this" {
cidr_block = var.agent_vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project_name}-${var.zone_name}-kasm-vpc"
Name = "${var.project_name}-${var.aws_region}-kasm-vpc"
}
}
data "aws_vpc" "data-kasm_agent_vpc" {
id = aws_vpc.kasm-agent-vpc.id
}
resource "aws_internet_gateway" "this" {
vpc_id = aws_vpc.this.id
resource "aws_internet_gateway" "kasm-default-ig" {
vpc_id = data.aws_vpc.data-kasm_agent_vpc.id
tags = {
Name = "${var.project_name}-${var.zone_name}-kasm-ig"
Name = "${var.project_name}-${var.aws_region}-kasm-ig"
}
}
data "aws_internet_gateway" "data-kasm_agent_default_ig" {
internet_gateway_id = aws_internet_gateway.kasm-default-ig.id
resource "aws_eip" "this" {
domain = "vpc"
}
resource "aws_nat_gateway" "this" {
allocation_id = aws_eip.this.id
subnet_id = aws_subnet.alb[0].id
tags = {
Name = "${var.project_name}-${var.aws_region}-kasm-nat"
}
depends_on = [aws_internet_gateway.this]
}

View file

@ -4,59 +4,69 @@
# agents/webapps that map to this region.
###########################################################
module "primary_region" {
source = "./primary"
aws_region = var.aws_primary_region
zone_name = var.aws_primary_region
vpc_subnet_cidr = var.primary_vpc_subnet_cidr
ec2_ami = var.primary_region_ec2_ami_id
db_instance_type = var.db_instance_type
num_webapps = var.num_webapps
project_name = var.project_name
kasm_build = var.kasm_build
db_hdd_size_gb = var.db_hdd_size_gb
swap_size = var.swap_size
database_password = var.database_password
redis_password = var.redis_password
user_password = var.user_password
admin_password = var.admin_password
manager_token = var.manager_token
service_registration_token = var.service_registration_token
aws_key_pair = var.aws_key_pair
aws_domain_name = var.aws_domain_name
ssh_access_cidrs = var.ssh_access_cidrs
web_access_cidrs = var.web_access_cidrs
source = "./primary"
aws_region = var.aws_primary_region
zone_name = var.aws_primary_region
vpc_subnet_cidr = var.primary_vpc_subnet_cidr
ec2_ami = var.primary_region_ec2_ami_id
db_instance_type = var.db_instance_type
num_webapps = var.num_webapps
num_cpx_nodes = var.num_cpx_nodes
project_name = var.project_name
kasm_build = var.kasm_build
db_hdd_size_gb = var.db_hdd_size_gb
swap_size = var.swap_size
database_password = var.database_password
redis_password = var.redis_password
user_password = var.user_password
admin_password = var.admin_password
manager_token = var.manager_token
service_registration_token = var.service_registration_token
aws_key_pair = var.aws_key_pair
aws_domain_name = var.aws_domain_name
web_access_cidrs = var.web_access_cidrs
create_aws_ssm_iam_role = var.create_aws_ssm_iam_role
aws_ssm_iam_role_name = var.aws_ssm_iam_role_name
aws_ssm_instance_profile_name = var.aws_ssm_instance_profile_name
}
module "primary_region-webapps-and-agents" {
module "primary_region_webapps_and_agents" {
source = "./webapps"
faux_aws_region = var.aws_primary_region
zone_name = var.aws_primary_region
primary_aws_region = var.aws_primary_region
load_balancer_subnet_ids = module.primary_region.lb_subnet_ids
num_webapps = var.num_webapps
num_agents = var.num_agents
num_cpx_nodes = var.num_cpx_nodes
ec2_ami = var.primary_region_ec2_ami_id
swap_size = var.swap_size
webapp_subnet_ids = module.primary_region.webapp_subnet_ids
webapp_security_group_id = module.primary_region.webapp_security_group_id
agent_subnet_id = module.primary_region.agent_subnet_id
agent_security_group_id = module.primary_region.agent_security_group_id
cpx_subnet_id = module.primary_region.cpx_subnet_id
cpx_security_group_id = module.primary_region.cpx_security_group_id
load_balancer_security_group_id = module.primary_region.lb_security_group_id
webapp_instance_type = var.webapp_instance_type
webapp_hdd_size_gb = var.webapp_hdd_size_gb
agent_instance_type = var.agent_instance_type
agent_hdd_size_gb = var.agent_hdd_size_gb
cpx_instance_type = var.cpx_instance_type
cpx_hdd_size_gb = var.cpx_hdd_size_gb
aws_domain_name = var.aws_domain_name
project_name = var.project_name
kasm_build = var.kasm_build
database_password = var.database_password
redis_password = var.redis_password
manager_token = var.manager_token
service_registration_token = var.service_registration_token
aws_key_pair = var.aws_key_pair
kasm_db_ip = module.primary_region.kasm_db_ip
primary_vpc_id = module.primary_region.primary_vpc_id
certificate_arn = module.primary_region.certificate_arn
ssh_access_cidrs = var.ssh_access_cidrs
load_balancer_log_bucket = module.primary_region.lb_log_bucket
aws_ssm_instance_profile_name = var.aws_ssm_instance_profile_name
}
#####################################################################
@ -64,11 +74,12 @@ module "primary_region-webapps-and-agents" {
# Add a webapp and agent module for each additional region desired.
#
#####################################################################
module "region2-webapps" {
module "region2_webapps" {
source = "./webapps"
faux_aws_region = var.secondary_regions_settings.region2.agent_region
zone_name = var.secondary_regions_settings.region2.agent_region
primary_aws_region = var.aws_primary_region
load_balancer_subnet_ids = module.primary_region.lb_subnet_ids
num_webapps = var.num_webapps
webapp_instance_type = var.webapp_instance_type
webapp_hdd_size_gb = var.webapp_hdd_size_gb
@ -87,26 +98,33 @@ module "region2-webapps" {
kasm_db_ip = module.primary_region.kasm_db_ip
primary_vpc_id = module.primary_region.primary_vpc_id
certificate_arn = module.primary_region.certificate_arn
ssh_access_cidrs = var.ssh_access_cidrs
load_balancer_log_bucket = module.primary_region.lb_log_bucket
aws_ssm_instance_profile_name = var.aws_ssm_instance_profile_name
}
module "region2-agents" {
source = "./agents"
aws_region = var.secondary_regions_settings.region2.agent_region
zone_name = var.secondary_regions_settings.region2.agent_region
num_agents = var.secondary_regions_settings.region2.num_agents
agent_instance_type = var.secondary_regions_settings.region2.agent_instance_type
ec2_ami = var.secondary_regions_settings.region2.agent_ec2_ami_id
agent_vpc_cidr = var.secondary_regions_settings.region2.agent_vpc_cidr
agent_hdd_size_gb = var.secondary_regions_settings.region2.agent_hdd_size_gb
swap_size = var.swap_size
aws_domain_name = var.aws_domain_name
project_name = var.project_name
kasm_build = var.kasm_build
manager_token = var.manager_token
aws_key_pair = var.aws_key_pair
ssh_access_cidrs = var.ssh_access_cidrs
module "region2_agents" {
source = "./agents"
aws_region = var.secondary_regions_settings.region2.agent_region
ec2_ami = var.secondary_regions_settings.region2.ec2_ami_id
agent_vpc_cidr = var.secondary_regions_settings.region2.agent_vpc_cidr
management_region_nat_gateway = module.primary_region.nat_gateway_ip
proxy_instance_type = var.proxy_instance_type
proxy_hdd_size_gb = var.proxy_hdd_size_gb
num_agents = var.num_agents
agent_instance_type = var.agent_instance_type
agent_hdd_size_gb = var.agent_hdd_size_gb
num_cpx_nodes = var.num_cpx_nodes
cpx_instance_type = var.cpx_instance_type
cpx_hdd_size_gb = var.cpx_hdd_size_gb
swap_size = var.swap_size
aws_domain_name = var.aws_domain_name
project_name = var.project_name
kasm_build = var.kasm_build
aws_key_pair = var.aws_key_pair
manager_token = var.manager_token
service_registration_token = var.service_registration_token
aws_ssm_instance_profile_name = var.aws_ssm_instance_profile_name
web_access_cidrs = var.web_access_cidrs
providers = {
aws = aws.region2
@ -116,17 +134,15 @@ module "region2-agents" {
#########################################################################
#
# Uncomment the below section and update the provider and the settings
# in the secondary_regions_settings variable in the settings.tfvars
# in the secondary_regions_settings variable in the terraform.tfvars
# file for your desired region.
#
#########################################################################
# module "region3-webapps" {
# module "region3_webapps" {
# source = "./webapps"
# ##### Update the values below to reference the appropriate region number
# faux_aws_region = var.secondary_regions_settings.region3.agent_region
# zone_name = var.secondary_regions_settings.region3.agent_region
#
# ##### The values below should not change
# load_balancer_subnet_ids = module.primary_region.lb_subnet_ids
# primary_aws_region = var.aws_primary_region
# num_webapps = var.num_webapps
# webapp_instance_type = var.webapp_instance_type
@ -146,31 +162,35 @@ module "region2-agents" {
# kasm_db_ip = module.primary_region.kasm_db_ip
# primary_vpc_id = module.primary_region.primary_vpc_id
# certificate_arn = module.primary_region.certificate_arn
# ssh_access_cidrs = var.ssh_access_cidrs
# load_balancer_log_bucket = module.primary_region.lb_log_bucket
# aws_ssm_instance_profile_name = var.aws_ssm_instance_profile_name
# }
# module "region3-agents" {
# source = "./agents"
# ##### Update the values below to reference the appropriate region number
# aws_region = var.secondary_regions_settings.region3.agent_region
# zone_name = var.secondary_regions_settings.region3.agent_region
# num_agents = var.secondary_regions_settings.region3.num_agents
# agent_instance_type = var.secondary_regions_settings.region3.agent_instance_type
# ec2_ami = var.secondary_regions_settings.region3.agent_ec2_ami_id
# agent_vpc_cidr = var.secondary_regions_settings.region3.agent_vpc_cidr
# agent_hdd_size_gb = var.secondary_regions_settings.region3.agent_hdd_size_gb
# swap_size = var.swap_size
#
# ##### The values below should not change
# aws_domain_name = var.aws_domain_name
# project_name = var.project_name
# kasm_build = var.kasm_build
# manager_token = var.manager_token
# aws_key_pair = var.aws_key_pair
# ssh_access_cidrs = var.ssh_access_cidrs
#
# ##### Update the provider to reference the settings in the provder.tf file
# module "region3_agents" {
# source = "./agents"
# aws_region = var.secondary_regions_settings.region3.agent_region
# ec2_ami = var.secondary_regions_settings.region3.ec2_ami_id
# agent_vpc_cidr = var.secondary_regions_settings.region3.agent_vpc_cidr
# load_balancer_log_bucket = module.primary_region.lb_log_bucket
# management_region_nat_gateway = module.primary_region.nat_gateway_ip
# proxy_instance_type = var.proxy_instance_type
# proxy_hdd_size_gb = var.proxy_hdd_size_gb
# num_agents = var.num_agents
# agent_instance_type = var.agent_instance_type
# agent_hdd_size_gb = var.agent_hdd_size_gb
# num_cpx_nodes = var.num_cpx_nodes
# cpx_instance_type = var.cpx_instance_type
# cpx_hdd_size_gb = var.cpx_hdd_size_gb
# swap_size = var.swap_size
# aws_domain_name = var.aws_domain_name
# project_name = var.project_name
# kasm_build = var.kasm_build
# aws_key_pair = var.aws_key_pair
# manager_token = var.manager_token
# service_registration_token = var.service_registration_token
# aws_ssm_instance_profile_name = var.aws_ssm_instance_profile_name
# web_access_cidrs = var.web_access_cidrs
# providers = {
# aws = aws.region3
# }

View file

@ -1,132 +1,92 @@
<mxfile host="65bd71144e">
<diagram id="evBy-dt-jB8v2-nSfg1o" name="AWS Multi-Region">
<mxGraphModel dx="1551" dy="1272" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="850" math="0" shadow="0">
<mxGraphModel dx="2406" dy="1532" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="850" background="#ffffff" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="15" value="WebApp Subnet 1&lt;br&gt;10.0.1.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" parent="1" vertex="1">
<mxGeometry x="460" y="189" width="130" height="261" as="geometry"/>
</mxCell>
<mxCell id="50" value="&lt;span style=&quot;color: rgb(0, 0, 0); font-size: 10px;&quot;&gt;US East&lt;br&gt;Webapp-1&lt;/span&gt;" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" parent="15" vertex="1">
<mxGeometry x="45" y="61" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="70" value="&lt;span style=&quot;color: rgb(0, 0, 0); font-size: 10px;&quot;&gt;US West&lt;br&gt;Webapp-1&lt;/span&gt;" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" parent="15" vertex="1">
<mxGeometry x="45" y="171" width="40" height="40" as="geometry"/>
<mxCell id="223" value="LB Security Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=1;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=4;" vertex="1" parent="1">
<mxGeometry x="320" y="1426" width="150" height="155" as="geometry"/>
</mxCell>
<mxCell id="2" value="AWS Account" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_account;strokeColor=#CD2264;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#CD2264;dashed=0;" parent="1" vertex="1">
<mxGeometry x="180" y="69" width="700" height="1101" as="geometry"/>
</mxCell>
<mxCell id="3" value="Mgmt VPC&lt;br&gt;10.0.0.0/16" style="sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_vpc;strokeColor=#879196;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#879196;dashed=0;" parent="2" vertex="1">
<mxGeometry x="158" y="47" width="502" height="703" as="geometry"/>
<mxGeometry x="180" y="69" width="820" height="1991" as="geometry"/>
</mxCell>
<mxCell id="20" value="Secondary Region - us-west-1" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_region;strokeColor=#147EBA;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#147EBA;dashed=1;" parent="2" vertex="1">
<mxGeometry x="120" y="771" width="300" height="320" as="geometry"/>
</mxCell>
<mxCell id="22" value="AZ 1a" style="sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_availability_zone;strokeColor=#545B64;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#545B64;dashed=1;" parent="20" vertex="1">
<mxGeometry x="80" y="101" width="180" height="200" as="geometry"/>
<mxGeometry x="80" y="1231" width="720" height="729" as="geometry"/>
</mxCell>
<mxCell id="21" value="Agent VPC&lt;br&gt;10.1.0.0/16" style="sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_vpc;strokeColor=#879196;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#879196;dashed=0;" parent="20" vertex="1">
<mxGeometry x="15" y="40" width="265" height="270" as="geometry"/>
<mxGeometry x="15" y="40" width="675" height="670" as="geometry"/>
</mxCell>
<mxCell id="79" value="Agent Security Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=1;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=4;" parent="20" vertex="1">
<mxGeometry x="95" y="130" width="150" height="155" as="geometry"/>
<mxCell id="22" value="AZ 1" style="sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_availability_zone;strokeColor=#545B64;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#545B64;dashed=1;" parent="20" vertex="1">
<mxGeometry x="220" y="92" width="200" height="608" as="geometry"/>
</mxCell>
<mxCell id="23" value="Agent Subnet&lt;br&gt;10.1.0.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" parent="20" vertex="1">
<mxGeometry x="100" y="150" width="140" height="130" as="geometry"/>
<mxCell id="193" value="AZ 2" style="sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_availability_zone;strokeColor=#545B64;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#545B64;dashed=1;" vertex="1" parent="20">
<mxGeometry x="450" y="92" width="200" height="418" as="geometry"/>
</mxCell>
<mxCell id="26" value="Internet&lt;br&gt;Gateway" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.internet_gateway;fillColor=#F58536;gradientColor=none;fontSize=10;" parent="20" vertex="1">
<mxGeometry x="30" y="80" width="38.34" height="40" as="geometry"/>
<mxCell id="244" value="LB Subnets&lt;br&gt;10.1.0.0/24&lt;br&gt;10.1.1.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" vertex="1" parent="20">
<mxGeometry x="73" y="143" width="120" height="129" as="geometry"/>
</mxCell>
<mxCell id="38" value="" style="group" parent="20" vertex="1" connectable="0">
<mxGeometry x="130" y="200" width="70" height="90" as="geometry"/>
<mxCell id="245" value="us-east-1&lt;br style=&quot;font-size: 10px;&quot;&gt;Load Balancer" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.application_load_balancer;fillColor=#F58534;gradientColor=none;fontSize=10;" vertex="1" parent="244">
<mxGeometry x="40" y="69" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="39" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" parent="38" vertex="1">
<mxCell id="194" value="Agent Security Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=1;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=4;" vertex="1" parent="20">
<mxGeometry x="475" y="340" width="150" height="155" as="geometry"/>
</mxCell>
<mxCell id="195" value="Agent Subnet&lt;br&gt;10.1.4.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" vertex="1" parent="20">
<mxGeometry x="480" y="360" width="140" height="130" as="geometry"/>
</mxCell>
<mxCell id="196" value="" style="group" vertex="1" connectable="0" parent="20">
<mxGeometry x="510" y="410" width="70" height="90" as="geometry"/>
</mxCell>
<mxCell id="197" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="196">
<mxGeometry x="5" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="40" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" parent="38" vertex="1">
<mxCell id="198" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="196">
<mxGeometry x="15" y="10" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="41" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" parent="38" vertex="1">
<mxCell id="199" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="196">
<mxGeometry x="25" y="20" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="42" value="Kasm Agents" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;" parent="38" vertex="1">
<mxGeometry y="60" width="70" height="30" as="geometry"/>
</mxCell>
<mxCell id="89" value="AZ 1a" style="sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_availability_zone;strokeColor=#545B64;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#545B64;dashed=1;" parent="2" vertex="1">
<mxGeometry x="260" y="67" width="160" height="673" as="geometry"/>
</mxCell>
<mxCell id="14" value="AZ 1b" style="sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_availability_zone;strokeColor=#545B64;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#545B64;dashed=1;" parent="2" vertex="1">
<mxGeometry x="450" y="67" width="160" height="333" as="geometry"/>
<mxCell id="254" value="NAT Gateway" style="outlineConnect=0;dashed=0;verticalLabelPosition=middle;verticalAlign=middle;align=right;html=1;shape=mxgraph.aws3.vpc_nat_gateway;fillColor=#F58536;gradientColor=none;labelBackgroundColor=none;fontColor=#4D4D4D;labelPosition=left;" vertex="1" parent="20">
<mxGeometry x="100" y="580" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="11" value="Primary Region - us-east-1" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=0;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_region;strokeColor=#147EBA;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#147EBA;dashed=1;" parent="2" vertex="1">
<mxGeometry x="120" y="20" width="560" height="740" as="geometry"/>
<mxGeometry x="80" y="27" width="720" height="1153" as="geometry"/>
</mxCell>
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;fillColor=#ffe6cc;strokeColor=#994C00;strokeWidth=3;" parent="2" source="4" target="6" edge="1">
<mxGeometry relative="1" as="geometry"/>
<mxCell id="3" value="Mgmt VPC&lt;br&gt;10.0.0.0/16" style="sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_vpc;strokeColor=#879196;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#879196;dashed=0;" parent="2" vertex="1">
<mxGeometry x="120" y="54" width="660" height="1116" as="geometry"/>
</mxCell>
<mxCell id="10" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;fillColor=#ffe6cc;strokeColor=#d79b00;strokeWidth=3;" parent="2" source="4" target="5" edge="1">
<mxGeometry relative="1" as="geometry"/>
<mxCell id="89" value="AZ 1" style="sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_availability_zone;strokeColor=#545B64;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#545B64;dashed=1;" parent="2" vertex="1">
<mxGeometry x="260" y="67" width="170" height="1093" as="geometry"/>
</mxCell>
<mxCell id="14" value="AZ 2" style="sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_availability_zone;strokeColor=#545B64;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#545B64;dashed=1;" parent="2" vertex="1">
<mxGeometry x="450" y="67" width="160" height="333" as="geometry"/>
</mxCell>
<mxCell id="16" value="WebApp Subnet 2&lt;br&gt;10.0.3.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" parent="2" vertex="1">
<mxGeometry x="460" y="120" width="130" height="260" as="geometry"/>
</mxCell>
<mxCell id="4" value="Route53 Latency&lt;br style=&quot;font-size: 10px;&quot;&gt;based DNS" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#945DF2;gradientDirection=north;fillColor=#5A30B5;strokeColor=#ffffff;dashed=0;verticalLabelPosition=top;verticalAlign=bottom;align=center;html=1;fontSize=10;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.route_53;labelPosition=center;" parent="2" vertex="1">
<mxGeometry x="29" y="240" width="50" height="50" as="geometry"/>
</mxCell>
<mxCell id="109" style="edgeStyle=orthogonalEdgeStyle;jumpStyle=arc;jumpSize=12;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeColor=#d6b656;strokeWidth=3;fontSize=10;fontColor=#000000;fillColor=#fff2cc;" parent="2" source="5" target="108" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="5" value="us-east-1&lt;br style=&quot;font-size: 10px;&quot;&gt;Load Balancer" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.application_load_balancer;fillColor=#F58534;gradientColor=none;fontSize=10;" parent="2" vertex="1">
<mxGeometry x="182" y="195" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="111" style="edgeStyle=orthogonalEdgeStyle;jumpStyle=arc;jumpSize=12;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeColor=#994C00;strokeWidth=3;fontSize=10;fontColor=#000000;" parent="2" source="6" target="110" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="6" value="us-west-1&lt;br&gt;Load Balancer" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.application_load_balancer;fillColor=#F58534;gradientColor=none;fontSize=10;" parent="2" vertex="1">
<mxGeometry x="182" y="307" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="76" value="Webapp Security&amp;nbsp; Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=1;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=3;" parent="2" vertex="1">
<mxGeometry x="270" y="100" width="330" height="290" as="geometry"/>
</mxCell>
<mxCell id="16" value="WebApp Subnet 2&lt;br&gt;10.0.2.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" parent="76" vertex="1">
<mxGeometry x="190" y="20" width="130" height="260" as="geometry"/>
</mxCell>
<mxCell id="110" value="" style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;fontSize=10;fillColor=none;strokeColor=#994C00;strokeWidth=2;dashed=1;arcSize=6;" parent="76" vertex="1">
<mxGeometry x="20" y="182" width="290" height="90" as="geometry"/>
</mxCell>
<mxCell id="72" value="&lt;span style=&quot;color: rgb(0, 0, 0); font-size: 10px;&quot;&gt;US West&lt;br&gt;Webapp-2&lt;/span&gt;" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" parent="2" vertex="1">
<mxGeometry x="510" y="290" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="71" value="&lt;span style=&quot;color: rgb(0, 0, 0); font-size: 10px;&quot;&gt;US East&lt;br&gt;Webapp-2&lt;/span&gt;" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" parent="2" vertex="1">
<mxGeometry x="510" y="180" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="121" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;fillColor=#e1d5e7;strokeColor=#9673a6;strokeWidth=3;" edge="1" parent="2" source="108" target="90">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="640" y="215"/>
<mxPoint x="640" y="478"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="108" value="" style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;fontSize=10;fillColor=none;strokeColor=#d6b656;strokeWidth=2;dashed=1;arcSize=6;" parent="2" vertex="1">
<mxGeometry x="290" y="170" width="290" height="90" as="geometry"/>
</mxCell>
<mxCell id="98" value="DB Subnet 1&lt;br&gt;10.0.0.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" parent="2" vertex="1">
<mxGeometry x="267.5" y="418" width="145" height="135" as="geometry"/>
<mxGeometry x="273.5" y="499" width="145" height="135" as="geometry"/>
</mxCell>
<mxCell id="120" value="Kasm Database&lt;br&gt;Kasm Redis" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" parent="98" vertex="1">
<mxGeometry x="50" y="49" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="123" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;strokeWidth=3;fillColor=#d5e8d4;strokeColor=#82b366;" edge="1" parent="2" source="91" target="4">
<mxCell id="190" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontSize=11;fontColor=#4D4D4D;" edge="1" parent="2" source="91" target="25">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="128" value="Agent to Management&lt;br&gt;WebApp traffic" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="123">
<mxGeometry x="-0.7263" y="-2" relative="1" as="geometry">
<mxPoint x="-52" y="2" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="91" value="Agent Security Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=1;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=2;" parent="2" vertex="1">
<mxGeometry x="265" y="567" width="150" height="152" as="geometry"/>
<mxGeometry x="270" y="821" width="150" height="152" as="geometry"/>
</mxCell>
<mxCell id="92" value="Agent Subnet 1&lt;br&gt;10.0.3.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" parent="2" vertex="1">
<mxGeometry x="270" y="586" width="140" height="130" as="geometry"/>
<mxGeometry x="275" y="840" width="140" height="130" as="geometry"/>
</mxCell>
<mxCell id="93" value="" style="group" parent="92" vertex="1" connectable="0">
<mxGeometry x="35" y="40" width="70" height="90" as="geometry"/>
@ -143,52 +103,119 @@
<mxCell id="97" value="Kasm Agents" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;" parent="93" vertex="1">
<mxGeometry y="60" width="70" height="30" as="geometry"/>
</mxCell>
<mxCell id="25" value="Internet&lt;br&gt;Gateway" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.internet_gateway;fillColor=#F58536;gradientColor=none;fontSize=10;" parent="2" vertex="1">
<mxGeometry x="172" y="93" width="28.76" height="30" as="geometry"/>
<mxCell id="25" value="Internet&lt;br&gt;Gateway" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.internet_gateway;fillColor=#F58536;gradientColor=none;fontSize=10;fontColor=#4D4D4D;" parent="2" vertex="1">
<mxGeometry x="700" y="851" width="37.39" height="39" as="geometry"/>
</mxCell>
<mxCell id="90" value="Database Security Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=1;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=4;" parent="2" vertex="1">
<mxGeometry x="265" y="400" width="150" height="155" as="geometry"/>
</mxCell>
<mxCell id="122" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;fillColor=#e1d5e7;strokeColor=#9673a6;" edge="1" parent="2" source="110" target="90">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="640" y="327"/>
<mxPoint x="640" y="478"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="124" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;strokeWidth=3;fillColor=#d5e8d4;strokeColor=#82b366;" edge="1" parent="2" source="79" target="4">
<mxCell id="183" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontSize=15;fontColor=#4D4D4D;" edge="1" parent="2" source="90" target="130">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="129" value="Agent to Management&lt;br&gt;WebApp traffic" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="124">
<mxGeometry x="-0.7506" relative="1" as="geometry">
<mxPoint x="20" as="offset"/>
<mxCell id="90" value="Database Security Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=1;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=4;" parent="2" vertex="1">
<mxGeometry x="271" y="481" width="150" height="155" as="geometry"/>
</mxCell>
<mxCell id="122" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;entryX=1;entryY=0.25;entryDx=0;entryDy=0;strokeWidth=3;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="2" target="90" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="640" y="350"/>
<mxPoint x="640" y="520"/>
</Array>
<mxPoint x="580" y="349.5" as="sourcePoint"/>
</mxGeometry>
</mxCell>
<mxCell id="102" value="Load Balancer&lt;br style=&quot;font-size: 10px;&quot;&gt;Access Logs S3" style="sketch=0;outlineConnect=0;fontColor=#232F3E;gradientColor=none;fillColor=#3F8624;strokeColor=none;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=10;fontStyle=0;aspect=fixed;pointerEvents=1;shape=mxgraph.aws4.bucket;labelBackgroundColor=none;" parent="2" vertex="1">
<mxGeometry x="595" y="674" width="32.68" height="34" as="geometry"/>
<mxCell id="130" value="NAT Gateway" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.vpc_nat_gateway;fillColor=#F58536;gradientColor=none;labelBackgroundColor=none;fontColor=#4D4D4D;" vertex="1" parent="2">
<mxGeometry x="699.06" y="538.5" width="38.33" height="40" as="geometry"/>
</mxCell>
<mxCell id="44" value="Management/User&lt;br&gt;Access Traffic" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.71;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;fontSize=10;strokeWidth=3;" parent="1" source="12" target="4" edge="1">
<mxCell id="162" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontColor=#4D4D4D;" edge="1" parent="2" source="152" target="130">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="270" value="To Internet" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=11;fontColor=#000000;labelBackgroundColor=#FFFFFF;" vertex="1" connectable="0" parent="162">
<mxGeometry x="0.6841" y="2" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="152" value="CPX Security Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=1;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=2;" vertex="1" parent="2">
<mxGeometry x="271" y="651" width="150" height="152" as="geometry"/>
</mxCell>
<mxCell id="153" value="CPX Subnet&lt;br&gt;10.0.4.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" vertex="1" parent="2">
<mxGeometry x="276" y="670" width="140" height="130" as="geometry"/>
</mxCell>
<mxCell id="154" value="" style="group" vertex="1" connectable="0" parent="153">
<mxGeometry x="35" y="40" width="70" height="90" as="geometry"/>
</mxCell>
<mxCell id="155" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="154">
<mxGeometry x="5" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="156" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="154">
<mxGeometry x="15" y="10" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="157" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="154">
<mxGeometry x="25" y="20" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="158" value="Kasm Agents" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;" vertex="1" parent="154">
<mxGeometry y="60" width="70" height="30" as="geometry"/>
</mxCell>
<mxCell id="191" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontSize=11;fontColor=#4D4D4D;" edge="1" parent="2" source="145" target="25">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="271" value="To Internet" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=11;fontColor=#000000;labelBackgroundColor=#FFFFFF;" vertex="1" connectable="0" parent="191">
<mxGeometry x="0.6863" y="4" relative="1" as="geometry">
<mxPoint x="1" y="4" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="145" value="Windows Security Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=1;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=2;" vertex="1" parent="2">
<mxGeometry x="269.5" y="991" width="150" height="152" as="geometry"/>
</mxCell>
<mxCell id="146" value="Agent Subnet 1&lt;br&gt;10.0.3.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" vertex="1" parent="2">
<mxGeometry x="274.5" y="1010" width="140" height="130" as="geometry"/>
</mxCell>
<mxCell id="147" value="" style="group" vertex="1" connectable="0" parent="146">
<mxGeometry x="35" y="40" width="70" height="90" as="geometry"/>
</mxCell>
<mxCell id="148" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="147">
<mxGeometry x="5" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="149" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="147">
<mxGeometry x="15" y="10" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="150" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="147">
<mxGeometry x="25" y="20" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="151" value="Kasm Agents" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;" vertex="1" parent="147">
<mxGeometry y="60" width="70" height="30" as="geometry"/>
</mxCell>
<mxCell id="102" value="Load Balancer&lt;br style=&quot;font-size: 10px;&quot;&gt;Access Logs S3" style="sketch=0;outlineConnect=0;fontColor=#232F3E;gradientColor=none;fillColor=#3F8624;strokeColor=none;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=10;fontStyle=0;aspect=fixed;pointerEvents=1;shape=mxgraph.aws4.bucket;labelBackgroundColor=none;" parent="2" vertex="1">
<mxGeometry x="715" y="1090" width="45" height="46.82" as="geometry"/>
</mxCell>
<mxCell id="173" value="LB Subnets&lt;br&gt;10.0.0.0/24&lt;br&gt;10.0.1.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" vertex="1" parent="2">
<mxGeometry x="134" y="134.5" width="120" height="231" as="geometry"/>
</mxCell>
<mxCell id="6" value="us-west-1&lt;br&gt;Load Balancer" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.application_load_balancer;fillColor=#F58534;gradientColor=none;fontSize=10;" parent="2" vertex="1">
<mxGeometry x="174" y="307" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="9" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;fillColor=#dae8fc;strokeColor=#6c8ebf;strokeWidth=3;" parent="2" source="4" target="6" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="15" value="WebApp Subnet 1&lt;br&gt;10.0.2.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" parent="1" vertex="1">
<mxGeometry x="460" y="189" width="130" height="261" as="geometry"/>
</mxCell>
<mxCell id="50" value="&lt;span style=&quot;color: rgb(0, 0, 0); font-size: 10px;&quot;&gt;US East&lt;br&gt;Webapp-1&lt;/span&gt;" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" parent="15" vertex="1">
<mxGeometry x="45" y="61" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="70" value="&lt;span style=&quot;color: rgb(0, 0, 0); font-size: 10px;&quot;&gt;US West&lt;br&gt;Webapp-1&lt;/span&gt;" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" parent="15" vertex="1">
<mxGeometry x="45" y="171" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="44" value="Management/User&lt;br&gt;Access Traffic" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.71;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;fontSize=10;strokeWidth=3;labelBackgroundColor=none;fontColor=#333333;strokeColor=#4D4D4D;" parent="1" source="12" target="4" edge="1">
<mxGeometry x="-0.1867" y="17" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="81" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.67;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;fontSize=10;fontColor=#000000;fillColor=#dae8fc;strokeColor=#6c8ebf;strokeWidth=3;entryX=0;entryY=0.75;entryDx=0;entryDy=0;" parent="1" source="12" target="79" edge="1">
<mxCell id="81" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.67;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;fontSize=10;fontColor=#000000;fillColor=#dae8fc;strokeColor=#6c8ebf;strokeWidth=3;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="12" target="245" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="150" y="920" as="targetPoint"/>
<mxPoint x="150" y="1120" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="127" value="Kasm User&lt;br&gt;SessionTraffic" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="81">
<mxCell id="127" value="Kasm User&lt;br&gt;SessionTraffic" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];labelBackgroundColor=none;fontColor=#333333;" parent="81" vertex="1" connectable="0">
<mxGeometry x="0.509" y="5" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="125" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.67;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;strokeWidth=3;fillColor=#dae8fc;strokeColor=#6c8ebf;jumpStyle=arc;jumpSize=12;" edge="1" parent="1" source="12" target="91">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="126" value="Kasm User&lt;br&gt;Session Traffic" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="125">
<mxGeometry x="0.2017" y="-5" relative="1" as="geometry">
<mxPoint as="offset"/>
<mxPoint x="39" y="71" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="12" value="&lt;font style=&quot;font-size: 10px;&quot;&gt;https://kasm.contoso.com&lt;/font&gt;" style="points=[[0.35,0,0],[0.98,0.51,0],[1,0.71,0],[0.67,1,0],[0,0.795,0],[0,0.65,0]];verticalLabelPosition=top;sketch=0;html=1;verticalAlign=bottom;aspect=fixed;align=center;pointerEvents=1;shape=mxgraph.cisco19.user;fillColor=#005073;strokeColor=none;fontColor=#005073;labelPosition=center;" parent="1" vertex="1">
@ -197,6 +224,211 @@
<mxCell id="119" value="&lt;u style=&quot;font-size: 16px;&quot;&gt;AWS Multi-Region Terraform Deployment&lt;/u&gt;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;dashed=1;labelBackgroundColor=none;strokeWidth=2;fontSize=16;fontColor=#000000;" parent="1" vertex="1">
<mxGeometry x="390" y="20" width="320" height="30" as="geometry"/>
</mxCell>
<mxCell id="163" value="" style="aspect=fixed;perimeter=ellipsePerimeter;html=1;align=center;shadow=0;dashed=0;spacingTop=3;image;image=img/lib/active_directory/internet_cloud.svg;labelBackgroundColor=none;fontColor=#4D4D4D;" vertex="1" parent="1">
<mxGeometry x="1060" y="770" width="160" height="100.8" as="geometry"/>
</mxCell>
<mxCell id="164" value="&lt;span style=&quot;font-family: Helvetica; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;&quot;&gt;Internet&lt;/span&gt;" style="text;whiteSpace=wrap;html=1;fontColor=#4D4D4D;labelBackgroundColor=none;align=center;verticalAlign=middle;fontSize=15;" vertex="1" parent="1">
<mxGeometry x="1105" y="800.4" width="70" height="40" as="geometry"/>
</mxCell>
<mxCell id="165" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontSize=15;fontColor=#4D4D4D;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="130" target="163">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="184" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;labelBackgroundColor=none;strokeColor=#d79b00;strokeWidth=3;fontSize=15;fontColor=#4D4D4D;fillColor=#ffe6cc;" edge="1" parent="1" source="175" target="180">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="175" value="us-east-1&lt;br style=&quot;font-size: 10px;&quot;&gt;Load Balancer" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.application_load_balancer;fillColor=#F58534;gradientColor=none;fontSize=10;" vertex="1" parent="1">
<mxGeometry x="354" y="264" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="172" value="LB Security&amp;nbsp; Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=0;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=3;" vertex="1" parent="1">
<mxGeometry x="309" y="186" width="123" height="254" as="geometry"/>
</mxCell>
<mxCell id="10" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;exitPerimeter=0;fillColor=#ffe6cc;strokeColor=#d79b00;strokeWidth=3;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="4" target="175" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="362" y="283.9999999999999" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="178" value="Webapp Security&amp;nbsp; Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=0;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=3;" vertex="1" parent="1">
<mxGeometry x="452" y="170" width="328" height="280" as="geometry"/>
</mxCell>
<mxCell id="180" value="" style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;fontSize=10;fillColor=none;strokeColor=#d6b656;strokeWidth=2;dashed=1;arcSize=6;" vertex="1" parent="1">
<mxGeometry x="480" y="240" width="270" height="90" as="geometry"/>
</mxCell>
<mxCell id="264" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.75;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0.25;entryDx=0;entryDy=0;labelBackgroundColor=none;strokeColor=#6c8ebf;strokeWidth=3;fontSize=11;fontColor=#000000;jumpStyle=arc;jumpSize=12;fillColor=#dae8fc;startArrow=classic;startFill=1;" edge="1" parent="1" source="181" target="91">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="265" style="edgeStyle=orthogonalEdgeStyle;jumpStyle=arc;jumpSize=12;html=1;exitX=0.75;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0.25;entryDx=0;entryDy=0;labelBackgroundColor=none;strokeColor=#6c8ebf;strokeWidth=3;fontSize=11;fontColor=#000000;fillColor=#dae8fc;startArrow=classic;startFill=1;" edge="1" parent="1" source="181" target="152">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="266" value="Kasm Management&lt;br&gt;to/from Agents, CPX&lt;br&gt;and Windows" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=11;fontColor=#000000;labelBackgroundColor=#FFFFFF;" vertex="1" connectable="0" parent="265">
<mxGeometry x="-0.6404" y="4" relative="1" as="geometry">
<mxPoint x="-59" y="-4" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="267" style="edgeStyle=orthogonalEdgeStyle;jumpStyle=arc;jumpSize=12;html=1;exitX=0.75;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0.25;entryDx=0;entryDy=0;labelBackgroundColor=none;strokeColor=#6c8ebf;strokeWidth=3;fontSize=11;fontColor=#000000;startArrow=classic;startFill=1;fillColor=#dae8fc;" edge="1" parent="1" source="181" target="145">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="181" value="" style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;fontSize=10;fillColor=none;strokeColor=#994C00;strokeWidth=2;dashed=1;arcSize=6;" vertex="1" parent="1">
<mxGeometry x="480" y="351" width="280" height="90" as="geometry"/>
</mxCell>
<mxCell id="182" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;labelBackgroundColor=none;strokeColor=#6c8ebf;strokeWidth=3;fontSize=15;fontColor=#4D4D4D;fillColor=#dae8fc;" edge="1" parent="1" source="6" target="181">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="160" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontColor=#4D4D4D;" edge="1" parent="1" source="181" target="130">
<mxGeometry relative="1" as="geometry">
<mxPoint x="760" y="396" as="sourcePoint"/>
</mxGeometry>
</mxCell>
<mxCell id="121" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;entryX=1;entryY=0.25;entryDx=0;entryDy=0;fillColor=#e1d5e7;strokeColor=#9673a6;strokeWidth=3;" parent="1" source="180" target="90" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="820" y="307"/>
<mxPoint x="820" y="589"/>
</Array>
<mxPoint x="760" y="306.5" as="sourcePoint"/>
</mxGeometry>
</mxCell>
<mxCell id="268" value="Kasm DB and Redis&lt;br&gt;Connections" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=11;fontColor=#000000;labelBackgroundColor=#FFFFFF;" vertex="1" connectable="0" parent="121">
<mxGeometry x="-0.697" y="4" relative="1" as="geometry">
<mxPoint x="13" y="27" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="159" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;strokeColor=#7A0000;fontColor=#4D4D4D;fillColor=#a20025;strokeWidth=3;" edge="1" parent="1" source="180" target="130">
<mxGeometry relative="1" as="geometry">
<mxPoint x="760" y="284" as="sourcePoint"/>
</mxGeometry>
</mxCell>
<mxCell id="269" value="To Internet" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=11;fontColor=#000000;labelBackgroundColor=#FFFFFF;" vertex="1" connectable="0" parent="159">
<mxGeometry x="0.4464" y="-1" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="192" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontSize=11;fontColor=#4D4D4D;" edge="1" parent="1" source="25" target="163">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="262" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontSize=11;fontColor=#000000;" edge="1" parent="1" source="26" target="260">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="26" value="&lt;font color=&quot;#000000&quot;&gt;Internet&lt;br&gt;Gateway&lt;/font&gt;" style="outlineConnect=0;dashed=0;verticalLabelPosition=middle;verticalAlign=middle;align=left;html=1;shape=mxgraph.aws3.internet_gateway;fillColor=#F58536;gradientColor=none;fontSize=10;labelPosition=right;" parent="1" vertex="1">
<mxGeometry x="790" y="1880" width="40" height="41.73" as="geometry"/>
</mxCell>
<mxCell id="251" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;labelBackgroundColor=none;strokeColor=#6c8ebf;strokeWidth=3;fontSize=11;fontColor=#4D4D4D;fillColor=#dae8fc;" edge="1" parent="1" source="209" target="216">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="259" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontSize=11;fontColor=#000000;" edge="1" parent="1" source="209">
<mxGeometry relative="1" as="geometry">
<mxPoint x="380" y="1880" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="209" value="CPX Security Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=1;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=4;" vertex="1" parent="1">
<mxGeometry x="512" y="1637" width="150" height="155" as="geometry"/>
</mxCell>
<mxCell id="210" value="CPX Subnet&lt;br&gt;10.1.5.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" vertex="1" parent="1">
<mxGeometry x="517" y="1657" width="140" height="130" as="geometry"/>
</mxCell>
<mxCell id="211" value="" style="group" vertex="1" connectable="0" parent="1">
<mxGeometry x="547" y="1707" width="70" height="90" as="geometry"/>
</mxCell>
<mxCell id="212" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="211">
<mxGeometry x="5" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="213" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="211">
<mxGeometry x="15" y="10" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="214" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="211">
<mxGeometry x="25" y="20" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="253" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontSize=11;fontColor=#000000;" edge="1" parent="1" source="216" target="26">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="216" value="Windows Security Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=1;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=4;" vertex="1" parent="1">
<mxGeometry x="512" y="1823" width="150" height="155" as="geometry"/>
</mxCell>
<mxCell id="217" value="Windows Subnet&lt;br&gt;10.1.6.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" vertex="1" parent="1">
<mxGeometry x="517" y="1843" width="140" height="130" as="geometry"/>
</mxCell>
<mxCell id="218" value="" style="group" vertex="1" connectable="0" parent="1">
<mxGeometry x="547" y="1893" width="70" height="90" as="geometry"/>
</mxCell>
<mxCell id="219" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="218">
<mxGeometry x="5" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="220" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="218">
<mxGeometry x="15" y="10" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="221" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="218">
<mxGeometry x="25" y="20" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="230" value="Proxy Security Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=1;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=4;" vertex="1" parent="1">
<mxGeometry x="507" y="1429" width="393" height="155" as="geometry"/>
</mxCell>
<mxCell id="231" value="Proxy Subnet&lt;br&gt;10.1.2.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" vertex="1" parent="1">
<mxGeometry x="512" y="1449" width="140" height="130" as="geometry"/>
</mxCell>
<mxCell id="232" value="" style="group" vertex="1" connectable="0" parent="1">
<mxGeometry x="542" y="1499" width="70" height="90" as="geometry"/>
</mxCell>
<mxCell id="233" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="232">
<mxGeometry x="5" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="234" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="232">
<mxGeometry x="15" y="10" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="235" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="232">
<mxGeometry x="25" y="20" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="237" value="" style="group" vertex="1" connectable="0" parent="1">
<mxGeometry x="745" y="1448" width="140" height="140" as="geometry"/>
</mxCell>
<mxCell id="224" value="Proxy Subnet&lt;br&gt;10.1.3.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" vertex="1" parent="237">
<mxGeometry width="140" height="130" as="geometry"/>
</mxCell>
<mxCell id="225" value="" style="group" vertex="1" connectable="0" parent="237">
<mxGeometry x="30" y="50" width="70" height="90" as="geometry"/>
</mxCell>
<mxCell id="226" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="225">
<mxGeometry x="5" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="227" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="225">
<mxGeometry x="15" y="10" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="228" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" vertex="1" parent="225">
<mxGeometry x="25" y="20" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="249" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.75;exitY=1;exitDx=0;exitDy=0;labelBackgroundColor=none;strokeColor=#6c8ebf;strokeWidth=3;fontSize=11;fontColor=#4D4D4D;fillColor=#dae8fc;" edge="1" parent="1" source="247">
<mxGeometry relative="1" as="geometry">
<mxPoint x="810" y="1640" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="250" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;labelBackgroundColor=none;strokeColor=#6c8ebf;strokeWidth=3;fontSize=11;fontColor=#4D4D4D;fillColor=#dae8fc;" edge="1" parent="1" source="247" target="209">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="258" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0;exitY=0.75;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontSize=11;fontColor=#000000;" edge="1" parent="1" source="247" target="254">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="490" y="1553"/>
<mxPoint x="490" y="1630"/>
<mxPoint x="380" y="1630"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="247" value="" style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;fontSize=10;fillColor=none;strokeColor=#d6b656;strokeWidth=2;dashed=1;arcSize=6;" vertex="1" parent="1">
<mxGeometry x="540" y="1490" width="310" height="84" as="geometry"/>
</mxCell>
<mxCell id="248" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;labelBackgroundColor=none;strokeColor=#6c8ebf;strokeWidth=3;fontSize=11;fontColor=#4D4D4D;fillColor=#dae8fc;" edge="1" parent="1" source="245" target="247">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="252" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontSize=11;fontColor=#000000;" edge="1" parent="1" source="194" target="26">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="260" value="" style="aspect=fixed;perimeter=ellipsePerimeter;html=1;align=center;shadow=0;dashed=0;spacingTop=3;image;image=img/lib/active_directory/internet_cloud.svg;labelBackgroundColor=none;fontColor=#4D4D4D;" vertex="1" parent="1">
<mxGeometry x="520" y="2075" width="160" height="100.8" as="geometry"/>
</mxCell>
<mxCell id="261" value="&lt;span style=&quot;font-family: Helvetica; font-size: 15px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;&quot;&gt;Internet&lt;/span&gt;" style="text;whiteSpace=wrap;html=1;fontColor=#4D4D4D;labelBackgroundColor=none;align=center;verticalAlign=middle;fontSize=15;" vertex="1" parent="1">
<mxGeometry x="565" y="2105.4" width="70" height="40" as="geometry"/>
</mxCell>
<mxCell id="263" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontSize=11;fontColor=#000000;" edge="1" parent="1" source="254" target="260">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

After

Width:  |  Height:  |  Size: 415 KiB

Before After
Before After

View file

@ -0,0 +1,33 @@
output "region1_zone_settings" {
description = "Upstream Auth and Proxy settings to apply to Kasm Primary Region Zone configuration"
value = <<ZONE
Kam Zone configuration for zone: ${module.primary_region_webapps_and_agents.kasm_zone_name}
Upstream Auth address: ${var.aws_domain_name}
Proxy address: ${join("", slice(split("-", var.aws_primary_region), 1, 3))}-lb.${var.aws_domain_name}
ZONE
}
output "region2_zone_settings" {
description = "Upstream Auth and Proxy settings to apply to Kasm Agent Region 2 Zone configuration"
value = <<ZONE
Kam Zone configuration for zone: ${module.primary_region_webapps_and_agents.kasm_zone_name}
Upstream Auth address: ${var.aws_domain_name}
Proxy address: ${join("", slice(split("-", var.secondary_regions_settings.region2.agent_region), 1, 3))}-proxy.${var.aws_domain_name}
ZONE
}
#########################################################################
#
# Uncomment the below section and update the provider and the settings
# in the secondary_regions_settings variable in the terraform.tfvars
# file for your desired region.
#
#########################################################################
# output "region3_zone_settings" {
# description = "Upstream Auth and Proxy settings to apply to Kasm Agent Region 3 Zone configuration"
# value = <<ZONE
# Kam Zone configuration for zone: default
# Upstream Auth address: ${var.aws_domain_name}
# Proxy address: ${join("", slice(split("-", var.secondary_regions_settings.region3.agent_region), 1, 3))}-proxy.${var.aws_domain_name}
# ZONE
# }

View file

@ -0,0 +1,136 @@
# primary
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 5.0 |
## Providers
| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | 5.36.0 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [aws_acm_certificate.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate) | resource |
| [aws_acm_certificate_validation.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation) | resource |
| [aws_eip.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource |
| [aws_iam_instance_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource |
| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_instance.db](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource |
| [aws_internet_gateway.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/internet_gateway) | resource |
| [aws_nat_gateway.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/nat_gateway) | resource |
| [aws_route53_record.certificate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_route_table.internet_gateway](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource |
| [aws_route_table.nat_gateway](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource |
| [aws_route_table_association.agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_route_table_association.alb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_route_table_association.cpx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_route_table_association.db](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_route_table_association.webapp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_route_table_association.windows](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_s3_bucket.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
| [aws_s3_bucket_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
| [aws_s3_bucket_public_access_block.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
| [aws_s3_bucket_server_side_encryption_configuration.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource |
| [aws_security_group.agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.cpx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.db](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.public_lb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.webapp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.windows](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group_rule.agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.agent_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.cpx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.cpx_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.db](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.db_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.public_lb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.public_lb_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.webapp_agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.webapp_cpx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.webapp_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.webapp_public_lb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.webapp_windows](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.windows](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.windows_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_subnet.agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_subnet.alb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_subnet.cpx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_subnet.db](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_subnet.webapp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_subnet.windows](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_vpc.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc) | resource |
| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source |
| [aws_elb_service_account.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/elb_service_account) | data source |
| [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_route53_zone.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route53_zone) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_admin_password"></a> [admin\_password](#input\_admin\_password) | The administrative user password. No special characters | `string` | n/a | yes |
| <a name="input_agent_security_rules"></a> [agent\_security\_rules](#input\_agent\_security\_rules) | A map of objects of security rules to apply to the Kasm WebApp server | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> }))</pre> | <pre>{<br> "https": {<br> "from_port": 443,<br> "protocol": "tcp",<br> "to_port": 443<br> }<br>}</pre> | no |
| <a name="input_anywhere"></a> [anywhere](#input\_anywhere) | Anywhere subnet for routing and load ingress from all IPs | `string` | `"0.0.0.0/0"` | no |
| <a name="input_aws_domain_name"></a> [aws\_domain\_name](#input\_aws\_domain\_name) | The Route53 Zone used for the dns entries. This must already exist in the AWS account. (e.g dev.kasm.contoso.com). The deployment will be accessed via this zone name via https | `string` | n/a | yes |
| <a name="input_aws_key_pair"></a> [aws\_key\_pair](#input\_aws\_key\_pair) | The name of an aws keypair to use. | `string` | n/a | yes |
| <a name="input_aws_region"></a> [aws\_region](#input\_aws\_region) | The AWS region for the deployment. (e.g us-east-1) | `string` | n/a | yes |
| <a name="input_aws_ssm_iam_role_name"></a> [aws\_ssm\_iam\_role\_name](#input\_aws\_ssm\_iam\_role\_name) | The name of the SSM EC2 role to associate with Kasm VMs for SSH access | `string` | `""` | no |
| <a name="input_aws_ssm_instance_profile_name"></a> [aws\_ssm\_instance\_profile\_name](#input\_aws\_ssm\_instance\_profile\_name) | The name of the SSM EC2 Instance Profile to associate with Kasm VMs for SSH access | `string` | `""` | no |
| <a name="input_cpx_security_rules"></a> [cpx\_security\_rules](#input\_cpx\_security\_rules) | A map of objects of security rules to apply to the Kasm Connection Proxy server | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> }))</pre> | <pre>{<br> "https": {<br> "from_port": 443,<br> "protocol": "tcp",<br> "to_port": 443<br> }<br>}</pre> | no |
| <a name="input_create_aws_ssm_iam_role"></a> [create\_aws\_ssm\_iam\_role](#input\_create\_aws\_ssm\_iam\_role) | Create an AWS SSM IAM role to attach to VMs for SSH/console access to VMs. | `bool` | `false` | no |
| <a name="input_database_password"></a> [database\_password](#input\_database\_password) | The password for the database. No special characters | `string` | n/a | yes |
| <a name="input_db_hdd_size_gb"></a> [db\_hdd\_size\_gb](#input\_db\_hdd\_size\_gb) | The HDD size in GB to configure for the Kasm Database instances | `number` | n/a | yes |
| <a name="input_db_instance_type"></a> [db\_instance\_type](#input\_db\_instance\_type) | The instance type for the Database | `string` | n/a | yes |
| <a name="input_db_security_rules"></a> [db\_security\_rules](#input\_db\_security\_rules) | A map of objects of security rules to apply to the Kasm DB | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> }))</pre> | <pre>{<br> "postgres": {<br> "from_port": 5432,<br> "protocol": "tcp",<br> "to_port": 5432<br> },<br> "redis": {<br> "from_port": 6379,<br> "protocol": "tcp",<br> "to_port": 6379<br> }<br>}</pre> | no |
| <a name="input_default_egress"></a> [default\_egress](#input\_default\_egress) | Default egress security rule for all security groups | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> cidr_subnets = list(string)<br> }))</pre> | <pre>{<br> "all": {<br> "cidr_subnets": [<br> "0.0.0.0/0"<br> ],<br> "from_port": 0,<br> "protocol": "-1",<br> "to_port": 0<br> }<br>}</pre> | no |
| <a name="input_ec2_ami"></a> [ec2\_ami](#input\_ec2\_ami) | The AMI used for the EC2 nodes. Recommended Ubuntu 22.04 LTS. | `string` | n/a | yes |
| <a name="input_kasm_build"></a> [kasm\_build](#input\_kasm\_build) | The URL for the Kasm Workspaces build | `string` | n/a | yes |
| <a name="input_manager_token"></a> [manager\_token](#input\_manager\_token) | The manager token value for Agents to authenticate to webapps. No special characters | `string` | n/a | yes |
| <a name="input_num_cpx_nodes"></a> [num\_cpx\_nodes](#input\_num\_cpx\_nodes) | The number of cpx RDP role servers to create in the deployment | `number` | n/a | yes |
| <a name="input_num_webapps"></a> [num\_webapps](#input\_num\_webapps) | The number of WebApp role servers to create in the deployment | `number` | n/a | yes |
| <a name="input_project_name"></a> [project\_name](#input\_project\_name) | The name of the deployment (e.g dev, staging). A short single word | `string` | n/a | yes |
| <a name="input_public_lb_security_rules"></a> [public\_lb\_security\_rules](#input\_public\_lb\_security\_rules) | A map of objects of security rules to apply to the Public ALB | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> }))</pre> | <pre>{<br> "http": {<br> "from_port": 80,<br> "protocol": "tcp",<br> "to_port": 80<br> },<br> "https": {<br> "from_port": 443,<br> "protocol": "tcp",<br> "to_port": 443<br> }<br>}</pre> | no |
| <a name="input_redis_password"></a> [redis\_password](#input\_redis\_password) | The password for the Redis server. No special characters | `string` | n/a | yes |
| <a name="input_service_registration_token"></a> [service\_registration\_token](#input\_service\_registration\_token) | The service registration token value for cpx RDP servers to authenticate to webapps. No special characters | `string` | n/a | yes |
| <a name="input_swap_size"></a> [swap\_size](#input\_swap\_size) | The amount of swap (in MB) to configure inside the compute instances | `number` | n/a | yes |
| <a name="input_user_password"></a> [user\_password](#input\_user\_password) | The standard (non administrator) user password. No special characters | `string` | n/a | yes |
| <a name="input_vpc_subnet_cidr"></a> [vpc\_subnet\_cidr](#input\_vpc\_subnet\_cidr) | The subnet CIDR to use for the Primary VPC | `string` | n/a | yes |
| <a name="input_web_access_cidrs"></a> [web\_access\_cidrs](#input\_web\_access\_cidrs) | List of Networks in CIDR notation for IPs allowed to access the Kasm Web interface | `list(string)` | n/a | yes |
| <a name="input_webapp_security_rules"></a> [webapp\_security\_rules](#input\_webapp\_security\_rules) | A map of objects of security rules to apply to the Kasm WebApp server | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> }))</pre> | <pre>{<br> "https": {<br> "from_port": 443,<br> "protocol": "tcp",<br> "to_port": 443<br> }<br>}</pre> | no |
| <a name="input_windows_security_rules"></a> [windows\_security\_rules](#input\_windows\_security\_rules) | A map of objects of security rules to apply to the Kasm Windows VMs | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> }))</pre> | <pre>{<br> "cpx_api": {<br> "from_port": 4902,<br> "protocol": "tcp",<br> "to_port": 4902<br> },<br> "cpx_rdp": {<br> "from_port": 3389,<br> "protocol": "tcp",<br> "to_port": 3389<br> },<br> "webapp_api": {<br> "from_port": 4902,<br> "protocol": "tcp",<br> "to_port": 4902<br> }<br>}</pre> | no |
| <a name="input_zone_name"></a> [zone\_name](#input\_zone\_name) | A name given to the kasm deployment Zone | `string` | `"default"` | no |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_agent_security_group_id"></a> [agent\_security\_group\_id](#output\_agent\_security\_group\_id) | Kasm Agent Primary region security group ID |
| <a name="output_agent_subnet_id"></a> [agent\_subnet\_id](#output\_agent\_subnet\_id) | Kasm Agent Primary region subnet ID |
| <a name="output_certificate_arn"></a> [certificate\_arn](#output\_certificate\_arn) | AWS Certificate manager certificate ARN |
| <a name="output_cpx_security_group_id"></a> [cpx\_security\_group\_id](#output\_cpx\_security\_group\_id) | Kasm Connection Proxy Primary region security group ID |
| <a name="output_cpx_subnet_id"></a> [cpx\_subnet\_id](#output\_cpx\_subnet\_id) | Kasm cpx RDP Primary region subnet ID |
| <a name="output_kasm_db_ip"></a> [kasm\_db\_ip](#output\_kasm\_db\_ip) | Kasm Database server subnet ID |
| <a name="output_lb_log_bucket"></a> [lb\_log\_bucket](#output\_lb\_log\_bucket) | Load balancer logging bucket name |
| <a name="output_lb_security_group_id"></a> [lb\_security\_group\_id](#output\_lb\_security\_group\_id) | Kasm Load balancer security group ID |
| <a name="output_lb_subnet_ids"></a> [lb\_subnet\_ids](#output\_lb\_subnet\_ids) | A list of the Public LB subnet IDs |
| <a name="output_nat_gateway_ip"></a> [nat\_gateway\_ip](#output\_nat\_gateway\_ip) | The NAT Gateway IP returned in CIDR notation for use with Windows security group rules |
| <a name="output_primary_vpc_id"></a> [primary\_vpc\_id](#output\_primary\_vpc\_id) | Kasm VPC ID |
| <a name="output_ssm_iam_profile"></a> [ssm\_iam\_profile](#output\_ssm\_iam\_profile) | The SSM IAM Instance Profile name |
| <a name="output_webapp_security_group_id"></a> [webapp\_security\_group\_id](#output\_webapp\_security\_group\_id) | Kasm Webapp security group ID |
| <a name="output_webapp_subnet_ids"></a> [webapp\_subnet\_ids](#output\_webapp\_subnet\_ids) | A list of the Kasm Webapp subnet IDs |
| <a name="output_windows_security_group_id"></a> [windows\_security\_group\_id](#output\_windows\_security\_group\_id) | Kasm Windows Primary region security group ID |
| <a name="output_windows_subnet_id"></a> [windows\_subnet\_id](#output\_windows\_subnet\_id) | Kasm Windows Primary region subnet ID |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

View file

@ -1,3 +0,0 @@
data "aws_availability_zones" "available" {
state = "available"
}

View file

@ -1,9 +1,4 @@
data "aws_route53_zone" "kasm-route53-zone" {
name = var.aws_domain_name
private_zone = false
}
resource "aws_acm_certificate" "kasm-alb-cert" {
resource "aws_acm_certificate" "this" {
domain_name = var.aws_domain_name
subject_alternative_names = ["*.${var.aws_domain_name}"]
validation_method = "DNS"
@ -14,9 +9,9 @@ resource "aws_acm_certificate" "kasm-alb-cert" {
}
}
resource "aws_route53_record" "kasm-route53-cert-validation-record" {
resource "aws_route53_record" "certificate" {
for_each = {
for dvo in aws_acm_certificate.kasm-alb-cert.domain_validation_options : dvo.domain_name => {
for dvo in aws_acm_certificate.this.domain_validation_options : dvo.domain_name => {
name = dvo.resource_record_name
record = dvo.resource_record_value
type = dvo.resource_record_type
@ -25,14 +20,14 @@ resource "aws_route53_record" "kasm-route53-cert-validation-record" {
name = each.value.name
type = each.value.type
records = [each.value.record]
zone_id = data.aws_route53_zone.kasm-route53-zone.id
zone_id = data.aws_route53_zone.this.id
ttl = 30
allow_overwrite = true
}
resource "aws_acm_certificate_validation" "kasm-elb-certificate-validation" {
certificate_arn = aws_acm_certificate.kasm-alb-cert.arn
validation_record_fqdns = [for record in aws_route53_record.kasm-route53-cert-validation-record : record.fqdn]
resource "aws_acm_certificate_validation" "this" {
certificate_arn = aws_acm_certificate.this.arn
validation_record_fqdns = [for record in aws_route53_record.certificate : record.fqdn]
}

View file

@ -1,9 +1,10 @@
resource "aws_instance" "kasm-db" {
resource "aws_instance" "db" {
ami = var.ec2_ami
instance_type = var.db_instance_type
vpc_security_group_ids = [data.aws_security_group.data-kasm_db_sg.id]
subnet_id = data.aws_subnet.data-kasm_db_subnet.id
vpc_security_group_ids = [aws_security_group.db.id]
subnet_id = aws_subnet.db.id
key_name = var.aws_key_pair
iam_instance_profile = var.create_aws_ssm_iam_role ? aws_iam_instance_profile.this[0].name : var.aws_ssm_instance_profile_name
root_block_device {
volume_size = var.db_hdd_size_gb
@ -11,21 +12,25 @@ resource "aws_instance" "kasm-db" {
user_data = templatefile("${path.module}/../userdata/db_bootstrap.sh",
{
kasm_build_url = var.kasm_build
user_password = var.user_password
admin_password = var.admin_password
redis_password = var.redis_password
database_password = var.database_password
manager_token = var.manager_token
swap_size = var.swap_size
kasm_build_url = var.kasm_build
user_password = var.user_password
admin_password = var.admin_password
redis_password = var.redis_password
database_password = var.database_password
manager_token = var.manager_token
service_registration_token = var.service_registration_token
swap_size = var.swap_size
}
)
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
instance_metadata_tags = null
}
tags = {
Name = "${var.project_name}-kasm-db"
}
}
data "aws_instance" "data-kasm_db" {
instance_id = aws_instance.kasm-db.id
}

View file

@ -0,0 +1,10 @@
data "aws_availability_zones" "available" {
state = "available"
}
data "aws_elb_service_account" "main" {}
data "aws_route53_zone" "this" {
name = var.aws_domain_name
private_zone = false
}

View file

@ -1,21 +1,10 @@
data "aws_elb_service_account" "main" {}
resource "aws_s3_bucket" "kasm_s3_logs" {
resource "aws_s3_bucket" "this" {
bucket_prefix = "${var.project_name}-${var.zone_name}-"
force_destroy = true
}
data "aws_s3_bucket" "data-kasm_s3_logs_bucket" {
bucket = aws_s3_bucket.kasm_s3_logs.bucket
}
resource "aws_s3_bucket_acl" "kasm_s3_acl" {
bucket = data.aws_s3_bucket.data-kasm_s3_logs_bucket.id
acl = "private"
}
resource "aws_s3_bucket_policy" "kasm_s3_logs_policy" {
bucket = data.aws_s3_bucket.data-kasm_s3_logs_bucket.id
resource "aws_s3_bucket_policy" "this" {
bucket = aws_s3_bucket.this.id
policy = jsonencode({
Id = "Policy"
@ -26,7 +15,7 @@ resource "aws_s3_bucket_policy" "kasm_s3_logs_policy" {
"s3:PutObject"
]
Effect = "Allow"
Resource = "${aws_s3_bucket.kasm_s3_logs.arn}/AWSLogs/*"
Resource = "${aws_s3_bucket.this.arn}/AWSLogs/*"
Principal = {
AWS = [
data.aws_elb_service_account.main.arn
@ -37,8 +26,8 @@ resource "aws_s3_bucket_policy" "kasm_s3_logs_policy" {
})
}
resource "aws_s3_bucket_server_side_encryption_configuration" "encrypt_elb_bucket" {
bucket = data.aws_s3_bucket.data-kasm_s3_logs_bucket.id
resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
bucket = aws_s3_bucket.this.id
rule {
apply_server_side_encryption_by_default {
@ -47,8 +36,8 @@ resource "aws_s3_bucket_server_side_encryption_configuration" "encrypt_elb_bucke
}
}
resource "aws_s3_bucket_public_access_block" "s3_log_public_access" {
bucket = data.aws_s3_bucket.data-kasm_s3_logs_bucket.id
resource "aws_s3_bucket_public_access_block" "this" {
bucket = aws_s3_bucket.this.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true

View file

@ -1,35 +1,79 @@
output "certificate_arn" {
value = aws_acm_certificate_validation.kasm-elb-certificate-validation.certificate_arn
description = "AWS Certificate manager certificate ARN"
value = aws_acm_certificate_validation.this.certificate_arn
}
output "lb_subnet_ids" {
description = "A list of the Public LB subnet IDs"
value = aws_subnet.alb[*].id
}
output "webapp_subnet_ids" {
value = data.aws_subnet.data-kasm_webapp_subnets[*].id
description = "A list of the Kasm Webapp subnet IDs"
value = aws_subnet.webapp[*].id
}
output "agent_subnet_id" {
value = data.aws_subnet.data-kasm_agent_subnet.id
description = "Kasm Agent Primary region subnet ID"
value = aws_subnet.agent.id
}
output "cpx_subnet_id" {
description = "Kasm cpx RDP Primary region subnet ID"
value = one(aws_subnet.cpx[*].id)
}
output "windows_subnet_id" {
description = "Kasm Windows Primary region subnet ID"
value = one(aws_subnet.windows[*].id)
}
output "kasm_db_ip" {
value = data.aws_instance.data-kasm_db.private_ip
description = "Kasm Database server subnet ID"
value = aws_instance.db.private_ip
}
output "primary_vpc_id" {
value = data.aws_vpc.data-kasm-default-vpc.id
description = "Kasm VPC ID"
value = aws_vpc.this.id
}
output "lb_log_bucket" {
value = data.aws_s3_bucket.data-kasm_s3_logs_bucket.bucket
description = "Load balancer logging bucket name"
value = aws_s3_bucket.this.bucket
}
output "lb_security_group_id" {
value = data.aws_security_group.data-kasm_default_elb_sg.id
description = "Kasm Load balancer security group ID"
value = aws_security_group.public_lb.id
}
output "webapp_security_group_id" {
value = data.aws_security_group.data-kasm_webapp_sg.id
description = "Kasm Webapp security group ID"
value = aws_security_group.webapp.id
}
output "agent_security_group_id" {
value = data.aws_security_group.data-kasm_agent_sg.id
description = "Kasm Agent Primary region security group ID"
value = aws_security_group.agent.id
}
output "cpx_security_group_id" {
description = "Kasm Connection Proxy Primary region security group ID"
value = one(aws_security_group.cpx[*].id)
}
output "windows_security_group_id" {
description = "Kasm Windows Primary region security group ID"
value = one(aws_security_group.windows[*].id)
}
output "ssm_iam_profile" {
description = "The SSM IAM Instance Profile name"
value = var.aws_ssm_iam_role_name == "" ? aws_iam_instance_profile.this[0].name : var.aws_ssm_iam_role_name
}
output "nat_gateway_ip" {
description = "The NAT Gateway IP returned in CIDR notation for use with Windows security group rules"
value = "${aws_nat_gateway.this.public_ip}/32"
}

View file

@ -1,8 +1,10 @@
terraform {
required_version = "~> 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
#version = "4.56.0"
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}

View file

@ -1,9 +1,9 @@
resource "aws_route_table" "internet_access" {
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
resource "aws_route_table" "internet_gateway" {
vpc_id = aws_vpc.this.id
route {
cidr_block = var.anywhere
gateway_id = data.aws_internet_gateway.data-kasm_default_ig.id
gateway_id = aws_internet_gateway.this.id
}
tags = {
@ -11,22 +11,51 @@ resource "aws_route_table" "internet_access" {
}
}
data "aws_route_table" "data-internet_gateway_route_table" {
route_table_id = aws_route_table.internet_access.id
resource "aws_route_table" "nat_gateway" {
vpc_id = aws_vpc.this.id
route {
cidr_block = var.anywhere
nat_gateway_id = aws_nat_gateway.this.id
}
tags = {
Name = "${var.project_name}-kasm-nat-gateway-route"
}
}
resource "aws_route_table_association" "webapp_route_association" {
resource "aws_route_table_association" "alb" {
count = 2
subnet_id = aws_subnet.alb[count.index].id
route_table_id = aws_route_table.internet_gateway.id
}
resource "aws_route_table_association" "webapp" {
count = var.num_webapps
subnet_id = data.aws_subnet.data-kasm_webapp_subnets[count.index].id
route_table_id = data.aws_route_table.data-internet_gateway_route_table.id
subnet_id = aws_subnet.webapp[count.index].id
route_table_id = aws_route_table.nat_gateway.id
}
resource "aws_route_table_association" "db_route_association" {
subnet_id = data.aws_subnet.data-kasm_db_subnet.id
route_table_id = data.aws_route_table.data-internet_gateway_route_table.id
resource "aws_route_table_association" "db" {
subnet_id = aws_subnet.db.id
route_table_id = aws_route_table.nat_gateway.id
}
resource "aws_route_table_association" "agent_table_association" {
subnet_id = data.aws_subnet.data-kasm_agent_subnet.id
route_table_id = data.aws_route_table.data-internet_gateway_route_table.id
resource "aws_route_table_association" "cpx" {
count = var.num_cpx_nodes > 0 ? 1 : 0
subnet_id = one(aws_subnet.cpx[*].id)
route_table_id = aws_route_table.nat_gateway.id
}
resource "aws_route_table_association" "agent" {
subnet_id = aws_subnet.agent.id
route_table_id = aws_route_table.internet_gateway.id
}
resource "aws_route_table_association" "windows" {
count = var.num_cpx_nodes > 0 ? 1 : 0
subnet_id = one(aws_subnet.windows[*].id)
route_table_id = aws_route_table.internet_gateway.id
}

View file

@ -1,145 +1,243 @@
resource "aws_security_group" "kasm-default-elb-sg" {
name = "${var.project_name}-${var.zone_name}-kasm-allow-elb-access"
resource "aws_security_group" "public_lb" {
name = "${var.project_name}-kasm-allow-public-lb-access"
description = "Security Group for ELB"
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = var.web_access_cidrs
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = var.web_access_cidrs
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = [var.anywhere]
}
vpc_id = aws_vpc.this.id
tags = {
Name = "${var.project_name}-${var.zone_name}-kasm-allow-access"
Name = "${var.project_name}-kasm-public-lb-access"
}
}
data "aws_security_group" "data-kasm_default_elb_sg" {
id = aws_security_group.kasm-default-elb-sg.id
resource "aws_security_group_rule" "public_lb" {
for_each = var.public_lb_security_rules
description = "Allow Public LB ingress from ${join(",", var.web_access_cidrs)}"
security_group_id = aws_security_group.public_lb.id
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = var.web_access_cidrs
}
resource "aws_security_group" "kasm-db-sg" {
name = "${var.project_name}-kasm-allow-db-access"
description = "Allow access to db"
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
resource "aws_security_group_rule" "public_lb_egress" {
for_each = var.default_egress
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = var.ssh_access_cidrs
}
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = data.aws_subnet.data-kasm_webapp_subnets[*].id
}
ingress {
from_port = 6379
to_port = 6379
protocol = "tcp"
cidr_blocks = data.aws_subnet.data-kasm_webapp_subnets[*].id
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = [var.anywhere]
}
tags = {
Name = "${var.project_name}-kasm-allow-db-access"
}
description = "Allow Public LB egress"
security_group_id = aws_security_group.public_lb.id
type = "egress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = each.value.cidr_subnets
}
data "aws_security_group" "data-kasm_db_sg" {
id = aws_security_group.kasm-db-sg.id
}
resource "aws_security_group" "kasm-webapp-sg" {
name = "${var.project_name}-${var.zone_name}-kasm-webapp-access"
resource "aws_security_group" "webapp" {
name = "${var.project_name}-kasm-webapp"
description = "Allow access to webapps"
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
vpc_id = aws_vpc.this.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = var.ssh_access_cidrs
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
security_groups = [data.aws_security_group.data-kasm_db_sg.id]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
security_groups = [data.aws_security_group.data-kasm_agent_sg.id]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = [var.anywhere]
tags = {
Name = "${var.project_name}-kasm-webapp-access"
}
}
data "aws_security_group" "data-kasm_webapp_sg" {
id = aws_security_group.kasm-webapp-sg.id
resource "aws_security_group_rule" "webapp_public_lb" {
for_each = var.webapp_security_rules
description = "Allow Webapp ingress from Public LB"
security_group_id = aws_security_group.webapp.id
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
source_security_group_id = aws_security_group.public_lb.id
}
resource "aws_security_group" "kasm-agent-sg" {
name = "${var.project_name}-${var.zone_name}-kasm-agent-access"
resource "aws_security_group_rule" "webapp_agent" {
for_each = var.webapp_security_rules
description = "Allow Webapp ingress from Kasm Agent"
security_group_id = aws_security_group.webapp.id
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
source_security_group_id = aws_security_group.agent.id
}
resource "aws_security_group_rule" "webapp_egress" {
for_each = var.default_egress
description = "Allow Webapp egress"
security_group_id = aws_security_group.webapp.id
type = "egress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = each.value.cidr_subnets
}
resource "aws_security_group" "agent" {
name = "${var.project_name}-kasm-agent-access"
description = "Allow access to agents"
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
vpc_id = aws_vpc.this.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = var.ssh_access_cidrs
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = [var.anywhere]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = [var.anywhere]
tags = {
Name = "${var.project_name}-kasm-agent-access"
}
}
data "aws_security_group" "data-kasm_agent_sg" {
id = aws_security_group.kasm-agent-sg.id
resource "aws_security_group_rule" "agent" {
for_each = var.agent_security_rules
description = "Allow Kasm Agent ingress from WebApps"
security_group_id = aws_security_group.agent.id
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
source_security_group_id = aws_security_group.webapp.id
}
resource "aws_security_group_rule" "agent_egress" {
for_each = var.default_egress
description = "Allow Agents egress"
security_group_id = aws_security_group.agent.id
type = "egress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = each.value.cidr_subnets
}
resource "aws_security_group" "db" {
name = "${var.project_name}-kasm-db-access"
description = "Allow access to webapps"
vpc_id = aws_vpc.this.id
tags = {
Name = "${var.project_name}-kasm-db-access"
}
}
resource "aws_security_group_rule" "db" {
for_each = var.db_security_rules
description = "Allow Kasm DB ingress from Kasm Webapp"
security_group_id = aws_security_group.db.id
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
source_security_group_id = aws_security_group.webapp.id
}
resource "aws_security_group_rule" "db_egress" {
for_each = var.default_egress
description = "Allow Kasm Db egress"
security_group_id = aws_security_group.db.id
type = "egress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = each.value.cidr_subnets
}
resource "aws_security_group" "cpx" {
count = var.num_cpx_nodes > 0 ? 1 : 0
name = "${var.project_name}-kasm-cpx-access"
description = "Allow access to cpx RDP nodes"
vpc_id = aws_vpc.this.id
tags = {
Name = "${var.project_name}-kasm-cpx-access"
}
}
resource "aws_security_group_rule" "cpx" {
for_each = var.num_cpx_nodes > 0 ? var.cpx_security_rules : {}
description = "Allow Kasm CPX ingress from Kasm Webapp"
security_group_id = one(aws_security_group.cpx[*].id)
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
source_security_group_id = aws_security_group.webapp.id
}
resource "aws_security_group_rule" "cpx_egress" {
for_each = var.num_cpx_nodes > 0 ? var.default_egress : {}
description = "Allow Kasm CPX egress"
security_group_id = one(aws_security_group.cpx[*].id)
type = "egress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = each.value.cidr_subnets
}
resource "aws_security_group_rule" "webapp_cpx" {
for_each = var.num_cpx_nodes > 0 ? var.webapp_security_rules : {}
description = "Allow Webapp ingress from Kasm CPX"
security_group_id = aws_security_group.webapp.id
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
source_security_group_id = one(aws_security_group.cpx[*].id)
}
resource "aws_security_group" "windows" {
count = var.num_cpx_nodes > 0 ? 1 : 0
name = "${var.project_name}-kasm-windows-access"
description = "Allow access to Windows servers"
vpc_id = aws_vpc.this.id
tags = {
Name = "${var.project_name}-kasm-windows-access"
}
}
resource "aws_security_group_rule" "windows" {
for_each = var.num_cpx_nodes > 0 ? var.windows_security_rules : {}
description = "Allow Windows ingress from Kasm CPX and WebApp"
security_group_id = one(aws_security_group.windows[*].id)
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
source_security_group_id = can(regex("(?i:cpx)", each.key)) ? one(aws_security_group.cpx[*].id) : aws_security_group.webapp.id
}
resource "aws_security_group_rule" "windows_egress" {
for_each = var.num_cpx_nodes > 0 ? var.default_egress : {}
description = "Allow Windows egress"
security_group_id = one(aws_security_group.windows[*].id)
type = "egress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = each.value.cidr_subnets
}
resource "aws_security_group_rule" "webapp_windows" {
for_each = var.num_cpx_nodes > 0 ? var.webapp_security_rules : {}
description = "Allow Windows ingress from Kasm WebApp"
security_group_id = aws_security_group.webapp.id
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
source_security_group_id = one(aws_security_group.windows[*].id)
}

View file

@ -0,0 +1,35 @@
data "aws_iam_policy_document" "this" {
statement {
effect = "Allow"
principals {
type = "Service"
identifiers = [
"ec2.amazonaws.com"
]
}
actions = ["sts:AssumeRole"]
}
}
resource "aws_iam_role" "this" {
count = var.create_aws_ssm_iam_role ? 1 : 0
name = var.aws_ssm_iam_role_name != "" ? var.aws_ssm_iam_role_name : "Kasm_SSM_IAM_Instance_Role"
assume_role_policy = data.aws_iam_policy_document.this.json
}
resource "aws_iam_role_policy_attachment" "this" {
count = var.create_aws_ssm_iam_role ? 1 : 0
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
role = one(aws_iam_role.this[*].name)
}
resource "aws_iam_instance_profile" "this" {
count = var.create_aws_ssm_iam_role ? 1 : 0
name = var.aws_ssm_instance_profile_name != "" ? var.aws_ssm_instance_profile_name : "Kasm_SSM_Instance_Profile"
role = one(aws_iam_role.this[*].name)
}

View file

@ -1,51 +1,77 @@
locals {
kasm_vpc_subnet_cidr_mask = split("/", var.vpc_subnet_cidr)[1]
kasm_server_subnet_cidr_calculation = (8 - (local.kasm_vpc_subnet_cidr_mask - 16))
kasm_server_subnet_cidr_size = local.kasm_server_subnet_cidr_calculation < 2 ? 2 : local.kasm_server_subnet_cidr_calculation
kasm_agent_subnet_id = (var.num_webapps + 1)
kasm_server_subnet_cidr_size = local.kasm_server_subnet_cidr_calculation < 3 ? 3 : local.kasm_server_subnet_cidr_calculation
}
## Will create Agent subnet x.x.0.0/24 (assuming a VPC Subnet CIDR between x.x.0.0/16 and x.x.0.0/22)
resource "aws_subnet" "kasm-db-subnet" {
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
cidr_block = cidrsubnet(var.vpc_subnet_cidr, local.kasm_server_subnet_cidr_size, 0)
map_public_ip_on_launch = true
## Will create Agent subnet x.x.0.0/24 and x.x.1.0/24 (assuming a VPC Subnet CIDR between x.x.0.0/16 and x.x.0.0/21)
resource "aws_subnet" "alb" {
count = 2
vpc_id = aws_vpc.this.id
cidr_block = cidrsubnet(var.vpc_subnet_cidr, local.kasm_server_subnet_cidr_size, count.index)
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {
Name = "${var.project_name}-kasm-db-subnet"
Name = "${var.project_name}-kasm_alb_subnet"
}
}
data "aws_subnet" "data-kasm_db_subnet" {
id = aws_subnet.kasm-db-subnet.id
}
## Will create WebApp subnets x.x.2.0/24 and x.x.3.0/24 (assuming a VPC Subnet CIDR between x.x.0.0/16 and x.x.0.0/22 and 2 webapps)
resource "aws_subnet" "webapp" {
count = var.num_webapps
vpc_id = aws_vpc.this.id
cidr_block = cidrsubnet(var.vpc_subnet_cidr, local.kasm_server_subnet_cidr_size, (count.index + 2))
availability_zone = data.aws_availability_zones.available.names[count.index]
## Will create WebApp subnets x.x.1.0/24 and x.x.2.0/24 (assuming a VPC Subnet CIDR between x.x.0.0/16 and x.x.0.0/22 and 2 webapps)
resource "aws_subnet" "kasm-webapp-subnets" {
count = var.num_webapps
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
cidr_block = cidrsubnet(var.vpc_subnet_cidr, local.kasm_server_subnet_cidr_size, (count.index + 1))
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-kasm-webapp-subnet"
}
}
data "aws_subnet" "data-kasm_webapp_subnets" {
count = var.num_webapps
id = aws_subnet.kasm-webapp-subnets[count.index].id
}
## Will create DB subnet x.x.4.0/24 (assuming a VPC Subnet CIDR between x.x.0.0/16 and x.x.0.0/22)
resource "aws_subnet" "db" {
vpc_id = aws_vpc.this.id
cidr_block = cidrsubnet(var.vpc_subnet_cidr, local.kasm_server_subnet_cidr_size, 4)
availability_zone = data.aws_availability_zones.available.names[0]
## Will create Agent subnet x.x.3.0/24 (assuming a VPC Subnet CIDR between x.x.0.0/16 and x.x.0.0/22)
resource "aws_subnet" "kasm-agent-subnet" {
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
cidr_block = cidrsubnet(var.vpc_subnet_cidr, local.kasm_server_subnet_cidr_size, local.kasm_agent_subnet_id)
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-agent-natgw-subnet"
Name = "${var.project_name}-kasm_db_subnet"
}
}
data "aws_subnet" "data-kasm_agent_subnet" {
id = aws_subnet.kasm-agent-subnet.id
## Will create Agent subnet x.x.3.0/24 (assuming a VPC Subnet CIDR between x.x.0.0/16 and x.x.0.0/22)
resource "aws_subnet" "agent" {
vpc_id = aws_vpc.this.id
cidr_block = cidrsubnet(var.vpc_subnet_cidr, local.kasm_server_subnet_cidr_size, 5)
availability_zone = data.aws_availability_zones.available.names[1]
tags = {
Name = "${var.project_name}-agent-subnet"
}
}
## Will create Agent subnet x.x.4.0/24 (assuming a VPC Subnet CIDR between x.x.0.0/16 and x.x.0.0/22)
resource "aws_subnet" "cpx" {
count = var.num_cpx_nodes > 0 ? 1 : 0
vpc_id = aws_vpc.this.id
cidr_block = cidrsubnet(var.vpc_subnet_cidr, local.kasm_server_subnet_cidr_size, 6)
availability_zone = data.aws_availability_zones.available.names[0]
tags = {
Name = "${var.project_name}-cpx-subnet"
}
}
## Will create Agent subnet x.x.5.0/24 (assuming a VPC Subnet CIDR between x.x.0.0/16 and x.x.0.0/22)
resource "aws_subnet" "windows" {
count = var.num_cpx_nodes > 0 ? 1 : 0
vpc_id = aws_vpc.this.id
cidr_block = cidrsubnet(var.vpc_subnet_cidr, local.kasm_server_subnet_cidr_size, 7)
availability_zone = data.aws_availability_zones.available.names[1]
tags = {
Name = "${var.project_name}-windows-subnet"
}
}

View file

@ -26,7 +26,6 @@ variable "db_hdd_size_gb" {
variable "db_instance_type" {
description = "The instance type for the Database"
type = string
default = "t3.small"
}
variable "swap_size" {
@ -70,7 +69,7 @@ variable "manager_token" {
}
variable "service_registration_token" {
description = "The guac token value for Guac RDP servers to authenticate to webapps. No special characters"
description = "The service registration token value for cpx RDP servers to authenticate to webapps. No special characters"
type = string
sensitive = true
}
@ -87,15 +86,10 @@ variable "aws_key_pair" {
}
variable "ec2_ami" {
description = "The AMI used for the EC2 nodes. Recommended Ubuntu 20.04 LTS."
description = "The AMI used for the EC2 nodes. Recommended Ubuntu 22.04 LTS."
type = string
}
variable "ssh_access_cidrs" {
description = "CIDR notation of the bastion host allowed to SSH in to the machines"
type = list(string)
}
variable "web_access_cidrs" {
description = "List of Networks in CIDR notation for IPs allowed to access the Kasm Web interface"
type = list(string)
@ -106,8 +100,172 @@ variable "num_webapps" {
type = number
}
variable "num_cpx_nodes" {
description = "The number of cpx RDP role servers to create in the deployment"
type = number
}
variable "create_aws_ssm_iam_role" {
description = "Create an AWS SSM IAM role to attach to VMs for SSH/console access to VMs."
type = bool
default = false
}
variable "aws_ssm_iam_role_name" {
description = "The name of the SSM EC2 role to associate with Kasm VMs for SSH access"
type = string
default = ""
}
variable "aws_ssm_instance_profile_name" {
description = "The name of the SSM EC2 Instance Profile to associate with Kasm VMs for SSH access"
type = string
default = ""
}
variable "anywhere" {
description = "Anywhere subnet for routing and load ingress from all IPs"
type = string
default = "0.0.0.0/0"
}
variable "public_lb_security_rules" {
description = "A map of objects of security rules to apply to the Public ALB"
type = map(object({
from_port = number
to_port = number
protocol = string
}))
default = {
https = {
from_port = 443
to_port = 443
protocol = "tcp"
}
http = {
from_port = 80
to_port = 80
protocol = "tcp"
}
}
}
variable "webapp_security_rules" {
description = "A map of objects of security rules to apply to the Kasm WebApp server"
type = map(object({
from_port = number
to_port = number
protocol = string
}))
default = {
https = {
from_port = 443
to_port = 443
protocol = "tcp"
}
}
}
variable "db_security_rules" {
description = "A map of objects of security rules to apply to the Kasm DB"
type = map(object({
from_port = number
to_port = number
protocol = string
}))
default = {
postgres = {
from_port = 5432
to_port = 5432
protocol = "tcp"
}
redis = {
from_port = 6379
to_port = 6379
protocol = "tcp"
}
}
}
variable "cpx_security_rules" {
description = "A map of objects of security rules to apply to the Kasm Connection Proxy server"
type = map(object({
from_port = number
to_port = number
protocol = string
}))
default = {
https = {
from_port = 443
to_port = 443
protocol = "tcp"
}
}
}
variable "agent_security_rules" {
description = "A map of objects of security rules to apply to the Kasm WebApp server"
type = map(object({
from_port = number
to_port = number
protocol = string
}))
default = {
https = {
from_port = 443
to_port = 443
protocol = "tcp"
}
}
}
variable "windows_security_rules" {
description = "A map of objects of security rules to apply to the Kasm Windows VMs"
type = map(object({
from_port = number
to_port = number
protocol = string
}))
default = {
cpx_rdp = {
from_port = 3389
to_port = 3389
protocol = "tcp"
}
cpx_api = {
from_port = 4902
to_port = 4902
protocol = "tcp"
}
webapp_api = {
from_port = 4902
to_port = 4902
protocol = "tcp"
}
}
}
variable "default_egress" {
description = "Default egress security rule for all security groups"
type = map(object({
from_port = number
to_port = number
protocol = string
cidr_subnets = list(string)
}))
default = {
all = {
from_port = 0
to_port = 0
protocol = "-1"
cidr_subnets = ["0.0.0.0/0"]
}
}
}

View file

@ -1,4 +1,4 @@
resource "aws_vpc" "kasm-default-vpc" {
resource "aws_vpc" "this" {
cidr_block = var.vpc_subnet_cidr
enable_dns_hostnames = true
enable_dns_support = true
@ -7,17 +7,24 @@ resource "aws_vpc" "kasm-default-vpc" {
}
}
data "aws_vpc" "data-kasm-default-vpc" {
id = aws_vpc.kasm-default-vpc.id
}
resource "aws_internet_gateway" "kasm-default-ig" {
vpc_id = aws_vpc.kasm-default-vpc.id
resource "aws_internet_gateway" "this" {
vpc_id = aws_vpc.this.id
tags = {
Name = "${var.project_name}-kasm-ig"
}
}
data "aws_internet_gateway" "data-kasm_default_ig" {
internet_gateway_id = aws_internet_gateway.kasm-default-ig.id
resource "aws_eip" "this" {
domain = "vpc"
}
resource "aws_nat_gateway" "this" {
allocation_id = aws_eip.this.id
subnet_id = aws_subnet.alb[0].id
tags = {
Name = "${var.project_name}-${var.aws_region}-kasm-nat"
}
depends_on = [aws_internet_gateway.this]
}

View file

@ -1,8 +1,10 @@
terraform {
required_version = "~> 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
#version = "4.56.0"
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
@ -31,7 +33,7 @@ provider "aws" {
##############################################################################
###
### Uncomment the below provider section if you want to deploy a 3rd region.
###
###
### Copy/paste the provider below to deploy additional regions, then refer
### to the README.md, the deployment.tf file, and the settings.tfvars file for
### code blocks to copy/paste/configure to deploy the new regions.

View file

@ -0,0 +1,2 @@
aws_access_key = ""
aws_secret_key = ""

View file

@ -1,62 +0,0 @@
aws_domain_name = "kasm.contoso.com"
project_name = "contoso"
aws_key_pair = ""
aws_primary_region = "us-east-1"
primary_vpc_subnet_cidr = "10.0.0.0/16"
database_password = "changeme"
redis_password = "changeme"
user_password = "changeme"
admin_password = "changeme"
manager_token = "changeme"
kasm_build = "https://kasm-static-content.s3.amazonaws.com/kasm_release_1.13.0.002947.tar.gz"
ssh_access_cidrs = ["0.0.0.0/0"]
web_access_cidrs = ["0.0.0.0/0"]
swap_size = 2048
primary_region_ec2_ami_id = "ami-09cd747c78a9add63"
webapp_instance_type = "t3.small"
db_instance_type = "t3.small"
agent_instance_type = "t3.medium"
webapp_hdd_size_gb = 40
db_hdd_size_gb = 40
agent_hdd_size_gb = 40
## Settings for all additional Agent regions
secondary_regions_settings = {
region2 = {
agent_region = "us-west-1"
agent_ec2_ami_id = "ami-0d221cb540e0015f4"
agent_instance_type = "t3.medium"
agent_hdd_size_gb = 120
num_agents = 2
agent_vpc_cidr = "10.1.0.0/16"
}
#######################################################################
###
### Uncomment and update the settings below for an third region.
### Copy/paste the settings below (changing the region number) for any
### additional regions.
###
### Make sure to add a provider section for each additional region in
### the providers.tf file.
###
#######################################################################
# region3 = {
# agent_region = "eu-central-1"
# agent_ec2_ami_id = "ami-0e067cc8a2b58de59"
# agent_instance_type = "t3.medium"
# num_agents = 2
# agent_vpc_cidr = "10.2.0.0/16"
# }
}
## Default tags for all AWS resources
aws_default_tags = {
Deployed_by = "Terraform"
Deployment_type = "Multi-Region"
Service_name = "Kasm Workspaces"
Kasm_version = "1.12"
}

View file

@ -0,0 +1,88 @@
## AWS Environment settings
aws_key_pair = ""
aws_primary_region = ""
aws_domain_name = "contoso.kasm.com"
primary_vpc_subnet_cidr = "10.0.0.0/16"
## Kasm deployment project
project_name = ""
## Kasm passwords
database_password = "changeme"
redis_password = "changeme"
user_password = "changeme"
admin_password = "changeme"
manager_token = "changeme"
service_registration_token = "changeme"
## Kasm download URL
kasm_build = "https://kasm-static-content.s3.amazonaws.com/kasm_release_1.14.0.3a7abb.tar.gz"
## VM Public Access subnets
web_access_cidrs = ["0.0.0.0/0"]
## AWS SSM setup for console/SSH access to VMs behind NAT gateway
create_aws_ssm_iam_role = true
aws_ssm_iam_role_name = ""
aws_ssm_instance_profile_name = ""
## Kasm Server Settings
swap_size = 2048
primary_region_ec2_ami_id = ""
## Kasm Webapp Instance Settings
num_webapps = 2
webapp_instance_type = "t3.small"
webapp_hdd_size_gb = 50
## Kasm DB Instance Settings
db_instance_type = "t3.medium"
db_hdd_size_gb = 80
## Kasm Agent Instance Settings
num_agents = 2
agent_instance_type = "t3.medium"
agent_hdd_size_gb = 150
## Kasm CPX Instance Settings
num_cpx_nodes = 1
cpx_instance_type = "t3.small"
cpx_hdd_size_gb = 50
## Kasm Dedicated Proxy Instance Settings
num_proxy_nodes = 2
proxy_hdd_size_gb = 40
proxy_instance_type = "t3.micro"
## Settings for all additional Agent regions
secondary_regions_settings = {
region2 = {
agent_region = ""
ec2_ami_id = ""
agent_vpc_cidr = "10.1.0.0/16"
}
#######################################################################
###
### Uncomment and update the settings below for an third region.
### Copy/paste the settings below (changing the region number) for any
### additional regions.
###
### Make sure to add a provider section for each additional region in
### the providers.tf file.
###
#######################################################################
# region3 = {
# agent_region = ""
# ec2_ami_id = ""
# agent_vpc_cidr = "10.2.0.0/16"
# }
}
## Default tags for all AWS resources
aws_default_tags = {
Deployed_by = "Terraform"
Deployment_type = "Multi-Region"
Service_name = "Kasm Workspaces"
Kasm_version = "1.14"
}

View file

@ -2,7 +2,7 @@
set -ex
echo "Starting Kasm Workspaces Agent Install"
/bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=${swap_size}
/bin/dd if=/dev/zero of=/var/swap.1 bs=1M count="${swap_size}"
/sbin/mkswap /var/swap.1
chmod 600 /var/swap.1
/sbin/swapon /var/swap.1
@ -24,6 +24,6 @@ do
done
echo "WebApp is alive"
bash kasm_release/install.sh -S agent -e -p $PRIVATE_IP -m ${manager_address} -M ${manager_token}
bash kasm_release/install.sh -S agent -e -p $PRIVATE_IP -m "${manager_address}" -M "${manager_token}"
echo "Done"

View file

@ -0,0 +1,29 @@
#!/bin/bash
set -ex
echo "Starting Kasm Workspaces Agent Install"
/bin/dd if=/dev/zero of=/var/swap.1 bs=1M count="${swap_size}"
/sbin/mkswap /var/swap.1
chmod 600 /var/swap.1
/sbin/swapon /var/swap.1
echo '/var/swap.1 swap swap defaults 0 0' | tee -a /etc/fstab
cd /tmp
PRIVATE_IP=(`hostname -I | cut -d ' ' -f1 | tr -d '\\n'`)
wget ${kasm_build_url} -O kasm_workspaces.tar.gz
tar -xf kasm_workspaces.tar.gz
echo "Waiting for Kasm WebApp availability..."
while ! (curl -k https://${manager_address}/api/__healthcheck 2>/dev/null | grep -q true)
do
echo "Waiting for API server..."
sleep 5
done
echo "WebApp is alive"
bash kasm_release/install.sh -S guac -e -p $PRIVATE_IP -n "${manager_address}" -k "${service_registration_token}"
echo "Done"

View file

@ -2,7 +2,7 @@
set -ex
echo "Starting Kasm Workspaces Install"
/bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=${swap_size}
/bin/dd if=/dev/zero of=/var/swap.1 bs=1M count="${swap_size}"
/sbin/mkswap /var/swap.1
chmod 600 /var/swap.1
/sbin/swapon /var/swap.1
@ -11,10 +11,8 @@ echo '/var/swap.1 swap swap defaults 0 0' | tee -a /etc/fstab
cd /tmp
PRIVATE_IP=(`hostname -I | cut -d ' ' -f1 | tr -d '\\n'`)
wget ${kasm_build_url} -O kasm_workspaces.tar.gz
wget "${kasm_build_url}" -O kasm_workspaces.tar.gz
tar -xf kasm_workspaces.tar.gz
bash kasm_release/install.sh -S db -e -Q ${database_password} -R ${redis_password} -U ${user_password} -P ${admin_password} -M ${manager_token}
bash kasm_release/install.sh -S db -e -Q "${database_password}" -R "${redis_password}" -U "${user_password}" -P "${admin_password}" -M "${manager_token}" -k "${service_registration_token}"
echo "Done"

View file

@ -0,0 +1,27 @@
#!/bin/bash
set -ex
echo "Starting Kasm Workspaces Agent Install"
/bin/dd if=/dev/zero of=/var/swap.1 bs=1M count="${swap_size}"
/sbin/mkswap /var/swap.1
chmod 600 /var/swap.1
/sbin/swapon /var/swap.1
echo '/var/swap.1 swap swap defaults 0 0' | tee -a /etc/fstab
cd /tmp
wget ${kasm_build_url} -O kasm_workspaces.tar.gz
tar -xf kasm_workspaces.tar.gz
echo "Waiting for Kasm WebApp availability..."
while ! (curl -k https://${manager_address}/api/__healthcheck 2>/dev/null | grep -q true)
do
echo "Waiting for API server..."
sleep 5
done
echo "WebApp is alive"
bash kasm_release/install.sh -S proxy -e -H -p "${proxy_alb_address}" -n "${manager_address}"
echo "Done"

View file

@ -2,7 +2,7 @@
set -ex
echo "Starting Kasm Workspaces Install"
/bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=${swap_size}
/bin/dd if=/dev/zero of=/var/swap.1 bs=1M count="${swap_size}"
/sbin/mkswap /var/swap.1
chmod 600 /var/swap.1
/sbin/swapon /var/swap.1
@ -11,26 +11,24 @@ echo '/var/swap.1 swap swap defaults 0 0' | tee -a /etc/fstab
cd /tmp
PRIVATE_IP=(`hostname -I | cut -d ' ' -f1 | tr -d '\\n'`)
wget ${kasm_build_url} -O kasm_workspaces.tar.gz
wget "${kasm_build_url}" -O kasm_workspaces.tar.gz
tar -xf kasm_workspaces.tar.gz
echo "Checking for Kasm DB and Redis..."
apt-get update && apt-get install -y netcat
while ! nc -w 1 -z ${db_ip} 5432; do
while ! nc -w 1 -z "${db_ip}" 5432; do
echo "Database not ready..."
sleep 5
done
echo "DB is alive"
while ! nc -w 1 -z ${db_ip} 6379; do
while ! nc -w 1 -z "${db_ip}" 6379; do
echo "Redis not ready..."
sleep 5
done
echo "Redis is alive"
bash kasm_release/install.sh -S app -e -z ${zone_name} -q "${db_ip}" -Q ${database_password} -R ${redis_password}
bash kasm_release/install.sh -S app -e -z "${zone_name}" -q "${db_ip}" -Q "${database_password}" -R "${redis_password}"
echo "Done"

View file

@ -30,6 +30,39 @@ variable "aws_key_pair" {
}
}
variable "create_aws_ssm_iam_role" {
description = "Create an AWS SSM IAM role to attach to VMs for SSH/console access to VMs."
type = bool
default = false
validation {
condition = can(tobool(var.create_aws_ssm_iam_role))
error_message = "The create_aws_ssm_iam_role is a boolean value and can only be either true or false."
}
}
variable "aws_ssm_iam_role_name" {
description = "The name of the SSM EC2 role to associate with Kasm VMs for SSH access"
type = string
default = ""
validation {
condition = can(regex("[a-zA-Z0-9+=,.@-]{1,64}", var.aws_ssm_iam_role_name))
error_message = "The aws_ssm_iam_role_name must be unique across the account and can only consisit of between 1 and 64 characters consisting of letters, numbers, underscores (_), plus (+), equals (=), comman (,), period (.), at symbol (@), or dash (-)."
}
}
variable "aws_ssm_instance_profile_name" {
description = "The name of the SSM EC2 Instance Profile to associate with Kasm VMs for SSH access"
type = string
default = ""
validation {
condition = var.aws_ssm_instance_profile_name == "" ? true : can(regex("[a-zA-Z0-9+=,.@-]{1,64}", var.aws_ssm_instance_profile_name))
error_message = "The aws_ssm_instance_profile_name must be unique across the account and can only consisit of between 1 and 64 characters consisting of letters, numbers, underscores (_), plus (+), equals (=), comman (,), period (.), at symbol (@), or dash (-)."
}
}
variable "project_name" {
description = "The name of the deployment (e.g dev, staging). A short single word"
type = string
@ -83,10 +116,30 @@ variable "num_webapps" {
}
}
variable "num_cpx_nodes" {
description = "The number of RDP Conection Proxy Role Servers to create in the deployment. Set this to zero (0) and this Terraform will not deploy ANY Connection Proxy or Windows resoures like subnets, security groups, etc."
type = number
validation {
condition = var.num_cpx_nodes == 0 ? true : var.num_cpx_nodes >= 0 && var.num_cpx_nodes <= 100 && floor(var.num_cpx_nodes) == var.num_cpx_nodes
error_message = "If num_cpx_nodes is set to 0, this Terraform will not deploy the Connection Proxy node. Acceptable number ranges between 0-100."
}
}
variable "num_proxy_nodes" {
description = "The number of Dedicated Proxy nodes to create in the deployment"
type = number
validation {
condition = var.num_proxy_nodes == 1 ? true : var.num_proxy_nodes >= 1 && var.num_proxy_nodes <= 100 && floor(var.num_proxy_nodes) == var.num_proxy_nodes
error_message = "The number of Dedicated Proxy nodes to deploy in remote regions. Acceptable number ranges between 1-100."
}
}
variable "webapp_instance_type" {
description = "The instance type for the webapps"
type = string
default = "t3.small"
default = ""
validation {
condition = can(regex("^(([a-z-]{1,3})(\\d{1,2})?(\\w{1,4})?)\\.(nano|micro|small|medium|metal|large|(2|3|4|6|8|9|10|12|16|18|24|32|48|56|112)?xlarge)", var.webapp_instance_type))
@ -97,7 +150,6 @@ variable "webapp_instance_type" {
variable "db_instance_type" {
description = "The instance type for the Database"
type = string
default = "t3.small"
validation {
condition = can(regex("^(([a-z-]{1,3})(\\d{1,2})?(\\w{1,4})?)\\.(nano|micro|small|medium|metal|large|(2|3|4|6|8|9|10|12|16|18|24|32|48|56|112)?xlarge)", var.db_instance_type))
@ -108,7 +160,6 @@ variable "db_instance_type" {
variable "agent_instance_type" {
description = "The instance type for the Agents"
type = string
default = "t3.medium"
validation {
condition = can(regex("^(([a-z-]{1,3})(\\d{1,2})?(\\w{1,4})?)\\.(nano|micro|small|medium|metal|large|(2|3|4|6|8|9|10|12|16|18|24|32|48|56|112)?xlarge)", var.agent_instance_type))
@ -116,6 +167,27 @@ variable "agent_instance_type" {
}
}
variable "cpx_instance_type" {
description = "The instance type for the Guac RDP nodes"
type = string
validation {
condition = can(regex("^(([a-z-]{1,3})(\\d{1,2})?(\\w{1,4})?)\\.(nano|micro|small|medium|metal|large|(2|3|4|6|8|9|10|12|16|18|24|32|48|56|112)?xlarge)", var.cpx_instance_type))
error_message = "Check the cpx_instance_type variable and ensure it is a valid AWS Instance type (https://aws.amazon.com/ec2/instance-types/)."
}
}
variable "proxy_instance_type" {
description = "The instance type for the dedicated proxy node"
type = string
default = ""
validation {
condition = can(regex("^(([a-z-]{1,3})(\\d{1,2})?(\\w{1,4})?)\\.(nano|micro|small|medium|metal|large|(2|3|4|6|8|9|10|12|16|18|24|32|48|56|112)?xlarge)", var.proxy_instance_type))
error_message = "Check the proxy_instance_type variable and ensure it is a valid AWS Instance type (https://aws.amazon.com/ec2/instance-types/)."
}
}
variable "webapp_hdd_size_gb" {
description = "The HDD size in GB to configure for the Kasm WebApp instances"
type = number
@ -146,10 +218,29 @@ variable "agent_hdd_size_gb" {
}
}
variable "cpx_hdd_size_gb" {
description = "The HDD size in GB to configure for the Kasm Guac RDP instances"
type = number
validation {
condition = can(var.cpx_hdd_size_gb >= 40)
error_message = "Kasm Guac RDP nodes should have at least a 40 GB HDD to ensure enough space for Kasm services."
}
}
variable "proxy_hdd_size_gb" {
description = "The HDD size in GB to configure for the Kasm dedicated proxy instances"
type = number
validation {
condition = can(var.proxy_hdd_size_gb >= 40)
error_message = "Kasm dedicated proxy nodes should have at least a 40 GB HDD to ensure enough space for Kasm services."
}
}
variable "primary_region_ec2_ami_id" {
description = "AMI Id of Kasm EC2 image in the primary region. Recommended AMI OS Version is Ubuntu 20.04 LTS."
type = string
default = "ami-09cd747c78a9add63"
validation {
condition = can(regex("^(ami-[a-f0-9]{17})", var.primary_region_ec2_ami_id))
@ -159,24 +250,21 @@ variable "primary_region_ec2_ami_id" {
variable "secondary_regions_settings" {
description = "Map of Kasm settings for secondary regions"
type = map(any)
type = map(object({
agent_region = string
agent_vpc_cidr = string
ec2_ami_id = string
})
)
validation {
condition = alltrue([for region in var.secondary_regions_settings : can(regex("^([a-z]{2}-[a-z]{4,}-[\\d]{1})$", region.agent_region))])
error_message = "Verify the regions in the secondary_regions_settings variable and ensure they are valid AWS regions in a valid format (e.g. us-east-1)."
}
validation {
condition = alltrue([for ami_id in var.secondary_regions_settings : can(regex("^(ami-[a-f0-9]{17})", ami_id.agent_ec2_ami_id))])
condition = alltrue([for ami_id in var.secondary_regions_settings : can(regex("^(ami-[a-f0-9]{17})", ami_id.ec2_ami_id))])
error_message = "Please verify that all of your Region's AMI IDs are in the correct format for AWS (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/finding-an-ami.html)."
}
validation {
condition = alltrue([for instance_type in var.secondary_regions_settings : can(regex("^(([a-z-]{1,3})(\\d{1,2})?(\\w{1,4})?)\\.(nano|micro|small|medium|metal|large|(2|3|4|6|8|9|10|12|16|18|24|32|48|56|112)?xlarge)", instance_type.agent_instance_type))])
error_message = "Check the Instance types used in your secondary_regions_settings and ensure they are valid AWS Instance types (https://aws.amazon.com/ec2/instance-types/)."
}
validation {
condition = alltrue([for number_of_agents in var.secondary_regions_settings : number_of_agents.num_agents >= 0 && number_of_agents.num_agents <= 100 && floor(number_of_agents.num_agents) == number_of_agents.num_agents])
error_message = "Check the number of agents in the secondary_regions_settings variable. Acceptable number of Kasm Agents range between 0-100."
}
validation {
condition = alltrue([for subnet in var.secondary_regions_settings : can(cidrhost(subnet.agent_vpc_cidr, 0))])
error_message = "Verify the VPC subnet in your secondary_regions_settings. They must all be valid IPv4 CIDRs."
@ -259,6 +347,17 @@ variable "manager_token" {
}
}
variable "service_registration_token" {
description = "The service registration token value for cpx RDP servers to authenticate to webapps. No special characters"
type = string
sensitive = true
validation {
condition = can(regex("^[a-zA-Z0-9]{12,30}$", var.service_registration_token))
error_message = "The Service Registration Token should be a string between 12 and 30 letters or numbers with no special characters."
}
}
variable "ssh_access_cidrs" {
description = "CIDR notation of the bastion host allowed to SSH in to the machines"
type = list(string)
@ -292,6 +391,6 @@ variable "aws_default_tags" {
type = map(any)
default = {
Service_name = "Kasm Workspaces"
Kasm_version = "1.12"
Kasm_version = "1.14"
}
}

View file

@ -0,0 +1,84 @@
# webapps
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 5.0 |
## Providers
| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | 5.36.0 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [aws_instance.agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource |
| [aws_instance.cpx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource |
| [aws_instance.webapp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource |
| [aws_lb.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb) | resource |
| [aws_lb_listener.http](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener) | resource |
| [aws_lb_listener.https](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener) | resource |
| [aws_lb_target_group.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_target_group) | resource |
| [aws_lb_target_group_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_target_group_attachment) | resource |
| [aws_route53_health_check.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_health_check) | resource |
| [aws_route53_record.alb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_route53_record.latency](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_route53_zone.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route53_zone) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_agent_hdd_size_gb"></a> [agent\_hdd\_size\_gb](#input\_agent\_hdd\_size\_gb) | The HDD size in GB to configure for the Kasm Agent instances | `number` | `0` | no |
| <a name="input_agent_instance_type"></a> [agent\_instance\_type](#input\_agent\_instance\_type) | the instance type for the agents | `string` | `""` | no |
| <a name="input_agent_security_group_id"></a> [agent\_security\_group\_id](#input\_agent\_security\_group\_id) | Kasm Agent security group ID | `string` | `""` | no |
| <a name="input_agent_subnet_id"></a> [agent\_subnet\_id](#input\_agent\_subnet\_id) | Subnet ID created for agents | `string` | `""` | no |
| <a name="input_aws_domain_name"></a> [aws\_domain\_name](#input\_aws\_domain\_name) | The Route53 Zone used for the dns entries. This must already exist in the AWS account. (e.g dev.kasm.contoso.com). The deployment will be accessed via this zone name via https | `string` | n/a | yes |
| <a name="input_aws_key_pair"></a> [aws\_key\_pair](#input\_aws\_key\_pair) | The name of an aws keypair to use. | `string` | n/a | yes |
| <a name="input_aws_ssm_instance_profile_name"></a> [aws\_ssm\_instance\_profile\_name](#input\_aws\_ssm\_instance\_profile\_name) | The name of the SSM EC2 Instance Profile to associate with Kasm VMs for SSH access | `string` | `""` | no |
| <a name="input_aws_to_kasm_zone_map"></a> [aws\_to\_kasm\_zone\_map](#input\_aws\_to\_kasm\_zone\_map) | AWS regions mapped to Kasm Deployment Zone names | `map(any)` | <pre>{<br> "af-south-1": "Africa-(Cape-Town)",<br> "ap-east-1": "China-(Hong-Kong)",<br> "ap-northeast-1": "Japan-(Tokyo)",<br> "ap-northeast-2": "S-Korea-(Seoul)",<br> "ap-northeast-3": "Japan-(Osaka)",<br> "ap-south-1": "India-(Mumbai)",<br> "ap-south-2": "India-(Hyderbad)",<br> "ap-southeast-1": "Singapore",<br> "ap-southeast-2": "Austrailia-(Sydney)",<br> "ap-southeast-3": "Indonesia-(Jakarta)",<br> "ap-southeast-4": "Austrailia-(Melbourne)",<br> "ca-central-1": "Canada-(Montreal)",<br> "eu-central-1": "Switzerland-(Zurich)",<br> "eu-north-1": "Sweden-(Stockholm)",<br> "eu-south-1": "Italy-(Milan)",<br> "eu-south-2": "Spain-(Aragon)",<br> "eu-west-1": "Ireland-(Dublin)",<br> "eu-west-2": "UK-(London)",<br> "eu-west-3": "France-(Paris)",<br> "me-central-1": "United-Arab-Emirates",<br> "me-south-1": "Manama-(Bahrain)",<br> "sa-east-1": "Brazil-(Sao-Paulo)",<br> "us-east-1": "USA-(Virginia)",<br> "us-east-2": "USA-(Ohio)",<br> "us-west-1": "USA-(California)",<br> "us-west-2": "USA-(Oregon)"<br>}</pre> | no |
| <a name="input_certificate_arn"></a> [certificate\_arn](#input\_certificate\_arn) | The certificate ARN created in the primary region for use with all load balancers in the deployment. | `string` | n/a | yes |
| <a name="input_cpx_hdd_size_gb"></a> [cpx\_hdd\_size\_gb](#input\_cpx\_hdd\_size\_gb) | The HDD size in GB to configure for the Kasm CPX instances | `number` | `0` | no |
| <a name="input_cpx_instance_type"></a> [cpx\_instance\_type](#input\_cpx\_instance\_type) | the instance type for the CPX nodes | `string` | `""` | no |
| <a name="input_cpx_security_group_id"></a> [cpx\_security\_group\_id](#input\_cpx\_security\_group\_id) | CPX security group ID | `string` | `""` | no |
| <a name="input_cpx_subnet_id"></a> [cpx\_subnet\_id](#input\_cpx\_subnet\_id) | Subnet ID created for Kasm CPX nodes | `string` | `""` | no |
| <a name="input_database_password"></a> [database\_password](#input\_database\_password) | The password for the database. No special characters | `string` | n/a | yes |
| <a name="input_ec2_ami"></a> [ec2\_ami](#input\_ec2\_ami) | The AMI used for the EC2 nodes. Recommended Ubuntu 20.04 LTS. | `string` | n/a | yes |
| <a name="input_faux_aws_region"></a> [faux\_aws\_region](#input\_faux\_aws\_region) | The AWS region this WebApp is supposed to represent even though it will be created in the primary region of the deployment. (e.g us-east-1) | `string` | n/a | yes |
| <a name="input_kasm_build"></a> [kasm\_build](#input\_kasm\_build) | The URL for the Kasm Workspaces build | `string` | n/a | yes |
| <a name="input_kasm_db_ip"></a> [kasm\_db\_ip](#input\_kasm\_db\_ip) | The IP/DNS name of the Kasm database | `string` | n/a | yes |
| <a name="input_load_balancer_log_bucket"></a> [load\_balancer\_log\_bucket](#input\_load\_balancer\_log\_bucket) | S3 bucket name for load balancers to forward access logs to | `string` | n/a | yes |
| <a name="input_load_balancer_security_group_id"></a> [load\_balancer\_security\_group\_id](#input\_load\_balancer\_security\_group\_id) | Security Group ID for the Primary region's load balancer | `string` | n/a | yes |
| <a name="input_load_balancer_subnet_ids"></a> [load\_balancer\_subnet\_ids](#input\_load\_balancer\_subnet\_ids) | ALB subnet IDs created to host webapps in the primary region | `list(string)` | n/a | yes |
| <a name="input_manager_token"></a> [manager\_token](#input\_manager\_token) | The Manager Token used by Kasm Agents to authenticate. No special characters | `string` | n/a | yes |
| <a name="input_num_agents"></a> [num\_agents](#input\_num\_agents) | The number of Agent Role Servers to create in the deployment | `number` | `0` | no |
| <a name="input_num_cpx_nodes"></a> [num\_cpx\_nodes](#input\_num\_cpx\_nodes) | The number of cpx Role Servers to create in the deployment | `number` | `0` | no |
| <a name="input_num_webapps"></a> [num\_webapps](#input\_num\_webapps) | The number of WebApp role servers to create in the deployment | `number` | n/a | yes |
| <a name="input_primary_aws_region"></a> [primary\_aws\_region](#input\_primary\_aws\_region) | The AWS region for primary region of the deployment. (e.g us-east-1) | `string` | n/a | yes |
| <a name="input_primary_vpc_id"></a> [primary\_vpc\_id](#input\_primary\_vpc\_id) | The VPC ID of the primary region | `string` | n/a | yes |
| <a name="input_project_name"></a> [project\_name](#input\_project\_name) | The name of the deployment (e.g dev, staging). A short single word | `string` | n/a | yes |
| <a name="input_redis_password"></a> [redis\_password](#input\_redis\_password) | The password for the database. No special characters | `string` | n/a | yes |
| <a name="input_service_registration_token"></a> [service\_registration\_token](#input\_service\_registration\_token) | The service registration token value for cpx RDP servers to authenticate to webapps. No special characters | `string` | `""` | no |
| <a name="input_swap_size"></a> [swap\_size](#input\_swap\_size) | The amount of swap (in MB) to configure inside the compute instances | `number` | n/a | yes |
| <a name="input_webapp_hdd_size_gb"></a> [webapp\_hdd\_size\_gb](#input\_webapp\_hdd\_size\_gb) | The HDD size in GB to configure for the Kasm WebApp instances | `number` | n/a | yes |
| <a name="input_webapp_instance_type"></a> [webapp\_instance\_type](#input\_webapp\_instance\_type) | The instance type for the webapps | `string` | n/a | yes |
| <a name="input_webapp_security_group_id"></a> [webapp\_security\_group\_id](#input\_webapp\_security\_group\_id) | WebApp security group ID | `string` | n/a | yes |
| <a name="input_webapp_subnet_ids"></a> [webapp\_subnet\_ids](#input\_webapp\_subnet\_ids) | WebApp subnet IDs created to host webapps in the primary region | `list(string)` | n/a | yes |
| <a name="input_zone_name"></a> [zone\_name](#input\_zone\_name) | A name given to the Kasm deployment Zone | `string` | n/a | yes |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_kasm_zone_name"></a> [kasm\_zone\_name](#output\_kasm\_zone\_name) | The zone name used for this region/zone in Kasm |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

View file

@ -1,10 +1,13 @@
resource "aws_instance" "kasm-agent" {
count = var.num_agents
ami = var.ec2_ami
instance_type = var.agent_instance_type
vpc_security_group_ids = [var.agent_security_group_id]
subnet_id = var.agent_subnet_id
key_name = var.aws_key_pair
resource "aws_instance" "agent" {
count = var.num_agents
ami = var.ec2_ami
instance_type = var.agent_instance_type
vpc_security_group_ids = [var.agent_security_group_id]
subnet_id = var.agent_subnet_id
key_name = var.aws_key_pair
associate_public_ip_address = true
iam_instance_profile = var.aws_ssm_instance_profile_name
root_block_device {
volume_size = var.agent_hdd_size_gb
@ -19,7 +22,14 @@ resource "aws_instance" "kasm-agent" {
}
)
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
instance_metadata_tags = null
}
tags = {
Name = "${var.project_name}-${var.zone_name}-kasm-agent"
Name = "${var.project_name}-${var.zone_name}-kasm-agent-${count.index}"
}
}

View file

@ -1,3 +0,0 @@
data "aws_availability_zones" "available" {
state = "available"
}

View file

@ -0,0 +1,34 @@
resource "aws_instance" "cpx" {
count = var.num_cpx_nodes
ami = var.ec2_ami
instance_type = var.cpx_instance_type
vpc_security_group_ids = [var.cpx_security_group_id]
subnet_id = var.cpx_subnet_id
key_name = var.aws_key_pair
iam_instance_profile = var.aws_ssm_instance_profile_name
root_block_device {
volume_size = var.cpx_hdd_size_gb
}
user_data = templatefile("${path.module}/../userdata/cpx_bootstrap.sh",
{
kasm_build_url = var.kasm_build
swap_size = var.swap_size
manager_address = var.aws_domain_name
service_registration_token = var.service_registration_token
}
)
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
instance_metadata_tags = null
}
tags = {
Name = "${var.project_name}-${var.primary_aws_region}-kasm-cpx-${count.index}"
}
}

View file

@ -0,0 +1,8 @@
locals {
region_short_name_for_lb = join("", slice(split("-", var.faux_aws_region), 1, 3))
}
data "aws_route53_zone" "this" {
name = var.aws_domain_name
private_zone = false
}

View file

@ -1,14 +1,9 @@
data "aws_route53_zone" "kasm-route53-zone" {
name = var.aws_domain_name
private_zone = false
}
resource "aws_lb" "kasm-alb" {
name = "${var.project_name}-lb"
resource "aws_lb" "this" {
name = "${var.project_name}-${var.faux_aws_region}-lb"
internal = false
load_balancer_type = "application"
security_groups = [var.load_balancer_security_group_id]
subnets = var.webapp_subnet_ids
subnets = var.load_balancer_subnet_ids
access_logs {
bucket = var.load_balancer_log_bucket
@ -16,41 +11,20 @@ resource "aws_lb" "kasm-alb" {
}
}
data "aws_lb" "data-kasm_alb" {
arn = aws_lb.kasm-alb.arn
}
resource "aws_lb_target_group" "kasm-target-group" {
name = "${var.project_name}-target-group"
port = 443
protocol = "HTTPS"
vpc_id = var.primary_vpc_id
health_check {
path = "/api/__healthcheck"
matcher = 200
protocol = "HTTPS"
}
}
data "aws_lb_target_group" "data-kasm_target_group" {
arn = aws_lb_target_group.kasm-target-group.arn
}
resource "aws_lb_listener" "kasm-alb-listener" {
load_balancer_arn = data.aws_lb.data-kasm_alb.arn
resource "aws_lb_listener" "https" {
load_balancer_arn = aws_lb.this.arn
port = "443"
protocol = "HTTPS"
certificate_arn = var.certificate_arn
default_action {
type = "forward"
target_group_arn = data.aws_lb_target_group.data-kasm_target_group.arn
target_group_arn = aws_lb_target_group.this.arn
}
}
resource "aws_lb_listener" "kasm_alb_listener_http" {
load_balancer_arn = data.aws_lb.data-kasm_alb.arn
resource "aws_lb_listener" "http" {
load_balancer_arn = aws_lb.this.arn
port = "80"
protocol = "HTTP"
@ -65,34 +39,47 @@ resource "aws_lb_listener" "kasm_alb_listener_http" {
}
}
resource "aws_lb_target_group_attachment" "kasm-target-group-attachment" {
count = var.num_webapps
target_group_arn = data.aws_lb_target_group.data-kasm_target_group.arn
target_id = data.aws_instance.data-kasm_web_apps[count.index].id
resource "aws_lb_target_group" "this" {
name = "${var.project_name}-${var.faux_aws_region}-tg"
port = 443
protocol = "HTTPS"
vpc_id = var.primary_vpc_id
health_check {
path = "/api/__healthcheck"
matcher = 200
protocol = "HTTPS"
}
}
resource "aws_lb_target_group_attachment" "this" {
count = var.num_webapps
target_group_arn = aws_lb_target_group.this.arn
target_id = aws_instance.webapp[count.index].id
port = 443
}
resource "aws_route53_record" "kasm-route53-elb-record" {
zone_id = data.aws_route53_zone.kasm-route53-zone.zone_id
name = "${var.zone_name}-lb.${var.aws_domain_name}"
resource "aws_route53_record" "alb" {
zone_id = data.aws_route53_zone.this.zone_id
name = "${local.region_short_name_for_lb}-lb.${var.aws_domain_name}"
type = "A"
alias {
name = data.aws_lb.data-kasm_alb.dns_name
zone_id = data.aws_lb.data-kasm_alb.zone_id
name = aws_lb.this.dns_name
zone_id = aws_lb.this.zone_id
evaluate_target_health = true
}
}
resource "aws_route53_record" "kasm-app-url" {
zone_id = data.aws_route53_zone.kasm-route53-zone.zone_id
resource "aws_route53_record" "latency" {
zone_id = data.aws_route53_zone.this.zone_id
name = var.aws_domain_name
type = "A"
set_identifier = "${var.project_name}-${var.zone_name}-set-id"
set_identifier = "${var.project_name}-${local.region_short_name_for_lb}-set-id"
alias {
name = data.aws_lb.data-kasm_alb.dns_name
zone_id = data.aws_lb.data-kasm_alb.zone_id
name = aws_lb.this.dns_name
zone_id = aws_lb.this.zone_id
evaluate_target_health = true
}
@ -101,8 +88,8 @@ resource "aws_route53_record" "kasm-app-url" {
}
}
resource "aws_route53_health_check" "kasm-elb-hc" {
fqdn = "${var.zone_name}-lb.${var.aws_domain_name}"
resource "aws_route53_health_check" "this" {
fqdn = "${local.region_short_name_for_lb}-lb.${var.aws_domain_name}"
port = 443
type = "HTTPS"
resource_path = "/api/__healthcheck"
@ -110,6 +97,6 @@ resource "aws_route53_health_check" "kasm-elb-hc" {
request_interval = "30"
tags = {
Name = "hc-${var.zone_name}-lb.${var.aws_domain_name}"
Name = "hc-${local.region_short_name_for_lb}-lb.${var.aws_domain_name}"
}
}

View file

@ -0,0 +1,4 @@
output "kasm_zone_name" {
description = "The zone name used for this region/zone in Kasm"
value = var.aws_to_kasm_zone_map[(var.faux_aws_region)]
}

View file

@ -1,8 +1,10 @@
terraform {
required_version = "~> 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
#version = "4.56.0"
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}

View file

@ -19,6 +19,12 @@ variable "agent_hdd_size_gb" {
default = 0
}
variable "cpx_hdd_size_gb" {
description = "The HDD size in GB to configure for the Kasm CPX instances"
type = number
default = 0
}
variable "database_password" {
description = "The password for the database. No special characters"
type = string
@ -31,6 +37,13 @@ variable "redis_password" {
sensitive = true
}
variable "service_registration_token" {
description = "The service registration token value for cpx RDP servers to authenticate to webapps. No special characters"
type = string
sensitive = true
default = ""
}
variable "manager_token" {
description = "The Manager Token used by Kasm Agents to authenticate. No special characters"
type = string
@ -45,13 +58,11 @@ variable "swap_size" {
variable "num_webapps" {
description = "The number of WebApp role servers to create in the deployment"
type = number
default = 2
}
variable "webapp_instance_type" {
description = "The instance type for the webapps"
type = string
default = "t3.small"
}
variable "webapp_security_group_id" {
@ -59,11 +70,28 @@ variable "webapp_security_group_id" {
type = string
}
variable "load_balancer_subnet_ids" {
description = "ALB subnet IDs created to host webapps in the primary region"
type = list(string)
}
variable "webapp_subnet_ids" {
description = "WebApp subnet IDs created to host webapps in the primary region"
type = list(string)
}
variable "cpx_instance_type" {
description = "the instance type for the CPX nodes"
type = string
default = ""
}
variable "cpx_subnet_id" {
description = "Subnet ID created for Kasm CPX nodes"
type = string
default = ""
}
variable "num_agents" {
description = "The number of Agent Role Servers to create in the deployment"
type = number
@ -73,7 +101,7 @@ variable "num_agents" {
variable "agent_instance_type" {
description = "the instance type for the agents"
type = string
default = "t3.medium"
default = ""
}
variable "agent_subnet_id" {
@ -98,6 +126,12 @@ variable "faux_aws_region" {
type = string
}
variable "num_cpx_nodes" {
description = "The number of cpx Role Servers to create in the deployment"
type = number
default = 0
}
variable "kasm_build" {
description = "The URL for the Kasm Workspaces build"
type = string
@ -128,11 +162,6 @@ variable "certificate_arn" {
type = string
}
variable "ssh_access_cidrs" {
description = "List of Networks in CIDR notation for IPs allowed to SSH in to the machines"
type = list(string)
}
variable "load_balancer_log_bucket" {
description = "S3 bucket name for load balancers to forward access logs to"
type = string
@ -143,7 +172,53 @@ variable "primary_vpc_id" {
type = string
}
variable "aws_ssm_instance_profile_name" {
description = "The name of the SSM EC2 Instance Profile to associate with Kasm VMs for SSH access"
type = string
default = ""
}
variable "load_balancer_security_group_id" {
description = "Security Group ID for the Primary region's load balancer"
type = string
}
variable "cpx_security_group_id" {
description = "CPX security group ID"
type = string
default = ""
}
variable "aws_to_kasm_zone_map" {
description = "AWS regions mapped to Kasm Deployment Zone names"
type = map(any)
default = {
us-east-1 = "USA-(Virginia)"
us-east-2 = "USA-(Ohio)"
us-west-1 = "USA-(California)"
us-west-2 = "USA-(Oregon)"
ap-south-1 = "India-(Mumbai)"
ap-northeast-3 = "Japan-(Osaka)"
ap-northeast-2 = "S-Korea-(Seoul)"
ap-southeast-1 = "Singapore"
ap-southeast-2 = "Austrailia-(Sydney)"
ap-northeast-1 = "Japan-(Tokyo)"
ca-central-1 = "Canada-(Montreal)"
eu-central-1 = "Germany-(Frankfurt)"
eu-west-1 = "Ireland-(Dublin)"
eu-west-2 = "UK-(London)"
eu-west-3 = "France-(Paris)"
eu-north-1 = "Sweden-(Stockholm)"
eu-south-1 = "Italy-(Milan)"
eu-south-2 = "Spain-(Aragon)"
eu-central-1 = "Switzerland-(Zurich)"
sa-east-1 = "Brazil-(Sao-Paulo)"
af-south-1 = "Africa-(Cape-Town)"
ap-east-1 = "China-(Hong-Kong)"
ap-south-2 = "India-(Hyderbad)"
ap-southeast-3 = "Indonesia-(Jakarta)"
ap-southeast-4 = "Austrailia-(Melbourne)"
me-south-1 = "Manama-(Bahrain)"
me-central-1 = "United-Arab-Emirates"
}
}

View file

@ -1,10 +1,12 @@
resource "aws_instance" "kasm-web-app" {
count = var.num_webapps
resource "aws_instance" "webapp" {
count = var.num_webapps
ami = var.ec2_ami
instance_type = var.webapp_instance_type
vpc_security_group_ids = [var.webapp_security_group_id]
subnet_id = var.webapp_subnet_ids[count.index]
key_name = var.aws_key_pair
iam_instance_profile = var.aws_ssm_instance_profile_name
root_block_device {
volume_size = var.webapp_hdd_size_gb
@ -17,16 +19,18 @@ resource "aws_instance" "kasm-web-app" {
database_password = var.database_password
redis_password = var.redis_password
swap_size = var.swap_size
zone_name = "default"
zone_name = var.aws_to_kasm_zone_map[(var.faux_aws_region)]
}
)
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
instance_metadata_tags = null
}
tags = {
Name = "${var.project_name}-${var.zone_name}-kasm-webapp"
Name = "${var.project_name}-${var.zone_name}-kasm-webapp-${count.index}"
}
}
data "aws_instance" "data-kasm_web_apps" {
count = var.num_webapps
instance_id = aws_instance.kasm-web-app[count.index].id
}

View file

@ -2,12 +2,13 @@
This project will deploy Kasm Workspaces in a multi-server deployment in AWS within a single region of your choice.
Each role is placed in a separate subnet and traffic from user sessions on the Agent egresses out of a Nat Gateway.
![Diagram][Image_Diagram]
[Image_Diagram]: https://f.hubspotusercontent30.net/hubfs/5856039/terraform/diagrams/aws-multi-server-nat-gw.png "Diagram"
[Image_Diagram]: https://5856039.fs1.hubspotusercontent-na1.net/hubfs/5856039/terraform/diagrams/aws-multi-server-new.jpg "Diagram"
> ***NOTE:*** This deployment has been tested and validated with both [Terraform](https://www.terraform.io/) and [OpenTofu](https://opentofu.org/)
# Pre-Configuration
Consider creating a special sub account for the Kasm deployment.
@ -27,55 +28,90 @@ Create a user via the IAM console that will be used for the terraform deployment
terraform init
2. Open `settings.tfvars` and update the variable values. The variable definitions, descriptions, and validation expectations can be found in the `variables.tf` file.
2. Open `terraform.tfvars` and update the variable values. The variable definitions, descriptions, and validation expectations can be found in the `variables.tf` file.
> ***NOTE:*** This document assumes you are using a separate file named `secrets.tfvars` for the AWS credentials generated in the [AWS API Keys](#aws-api-keys) section above. The .gitignore file in this repository will ignore any files named `secrets.tfvars` since they are expected to have sensitive values in them. This will prevent you from accidentally committing them to source control.
> ***NOTE:*** This document assumes you are using a separate file named `secrets.tfvars` for the AWS credentials generated in the [AWS API Keys](#aws-api-keys) section above. The .gitignore file in this repository will ignore any files named `secrets.tfvars` since they are expected to have sensitive values in them. This will prevent you from accidentally committing them to source control. If you would rather use Environment variables or some other AWS credential method in lieu of the `secrets.tfvars` file, check out the [AWS Terraform provider documentation](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#environment-variables) for more information about configuring your environment.
3. Verify the configuration
terraform plan -var-file settings.tfvars -var-file secrets.tfvars
terraform plan -var-file secrets.tfvars
4. Deploy
terraform apply -var-file settings.tfvars -var-file secrets.tfvars
terraform apply -var-file secrets.tfvars
5. Login to the Deployment as an Admin via the domain defined e.g `https://kasm.contoso.com`
5. Login to the Deployment as an Admin via the domain defined; e.g., `https://kasm.contoso.com`
6. Navigate to the Agents tab, and enable each Agent after it checks in. (May take a few minutes)
7. Now you are ready to add Workspaces via the registry and start using Kasm!
# AWS Terraform Variable definitions
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Variable | Description | Variable type | Example |
|:--------:|-------------|---------------|---------|
| `aws_access_key` | The AWS access key used for deployment. | String | `"AKIAJSIE27KKMHXI3BJQ"` |
| `aws_secret_key` | The AWS secret key used for deployment. | String | `"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"` |
| `aws_region` | The AWS Region used for deployment. | String | `"us-east-1"` |
| `project_name` | The name of the deployment (e.g dev, staging). A short single word of up to 15 characters. | String | `"kasm"` |
| `aws_domain_name` | The Route53 Zone used for the dns entries. This must already exist in the AWS account. (e.g dev.kasm.contoso.com). The deployment will be accessed via this zone name via https. | String | `"kasm.contoso.com"` |
| `kasm_zone_name` | A name given to the kasm deployment Zone. | String | `"default"` |
| `vpc_subnet_cidr` | The subnet CIDR to use for the VPC | String | `"10.0.0.0/16"` |
| `aws_key_pair` | The name of an aws keypair to use. | String | `"kasm_ssh_key"` |
| `ec2_ami` | The AMI used for the EC2 nodes. Recommended Ubuntu 20.04 LTS. | String | `"ami-09cd747c78a9add63"` |
| `swap_size` | The amount of swap (in MB) to configure inside the Kasm servers. | Number | `2048` |
| `webapp_instance_type` | The instance type for the webapps. | String | `"t3.small"` |
| `db_instance_type` | The instance type for the webapps. | String | `"t3.medium"` |
| `agent_instance_type` | The instance type for the webapps. | String | `"t3.medium"` |
| `guac_instance_type` | The instance type for the webapps. | String | `"t3.medium"` |
| `num_webapps` | The number of WebApp role servers to create in this deployment. Acceptable ranges from 1-3. | Number | `2` |
| `num_agents` | The number of static Kasm Agents to create in this deploymenbt. Acceptable ranges from 0-100. | Number | `2` |
| `num_guac_rdp_nodes` | The number of Guacamole RDP access servers to create in this deployment. Acceptable ranges from 0-100. | Number | `1` |
| `allow_ssh_cidrs` | A list of subnets in CIDR notation allowed to SSH into your kasm servers | List(String) | `["10.0.0.0/16","172.217.22.14/32"]` |
| `web_access_cidrs` | A list of subnets in CIDR notation allowed Web access to your kasm servers | List(String) | `["0.0.0.0/0"]` |
| `database_password` | The Kasm PostgreSQL database password. String from 12-30 characters in length with no special characters. | String | `"1qaz2wsx3EDC4RFV"` |
| `redis_password` | The Kasm Redis password. String from 12-30 characters in length with no special characters. | String | `"1qaz2wsx3EDC4RFV"` |
| `admin_password` | The Kasm Administrative user login password. String from 12-30 characters in length with no special characters. | String | `"1qaz2wsx3EDC4RFV"` |
| `user_password` | A Kasm standard (non-administrator) user password. String from 12-30 characters in length with no special characters. | String | `"1qaz2wsx3EDC4RFV"` |
| `manager_token` | The manager token value used by Kasm agents to authenticate to the Kasm WebApps. String from 12-30 characters in length with no special characters. | String | `"1qaz2wsx3EDC4RFV"` |
| `service_registration_token` | The service registration token value used by Guac RDP servers to authenticate to the Kasm Webapps. String from 12-30 characters in length with no special characters. | String | `"1qaz2wsx3EDC4RFV"` |
| `kasm_build` | The download URL for the desired Kasm Workspaces version. | String | `"https://kasm-static-content.s3.amazonaws.com/kasm_release_1.13.0.002947.tar.gz"` |
| `aws_default_tags` | A Map of all tags you wish to apply to all TF created resources in this deployment. | Map(Any) | <pre align=left>{<br/>&nbsp;&nbsp;Service_name = "Kasm Workspaces"<br/>&nbsp;&nbsp;Kasm_version = "1.12"<br/>}</pre> |
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 5.0 |
## Providers
No providers.
## Modules
| Name | Source | Version |
|------|--------|---------|
| <a name="module_standard"></a> [standard](#module\_standard) | ./module | n/a |
## Resources
No resources.
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_admin_password"></a> [admin\_password](#input\_admin\_password) | The administrative user password. No special characters | `string` | n/a | yes |
| <a name="input_agent_hdd_size_gb"></a> [agent\_hdd\_size\_gb](#input\_agent\_hdd\_size\_gb) | The HDD size in GB to configure for the Kasm Agent instances | `number` | n/a | yes |
| <a name="input_agent_instance_type"></a> [agent\_instance\_type](#input\_agent\_instance\_type) | The instance type for the Agents | `string` | n/a | yes |
| <a name="input_aws_access_key"></a> [aws\_access\_key](#input\_aws\_access\_key) | The AWS access key used for deployment | `string` | n/a | yes |
| <a name="input_aws_default_tags"></a> [aws\_default\_tags](#input\_aws\_default\_tags) | Default tags to apply to all AWS resources for this deployment | `map(any)` | `{}` | no |
| <a name="input_aws_domain_name"></a> [aws\_domain\_name](#input\_aws\_domain\_name) | The Route53 Zone used for the dns entries. This must already exist in the AWS account. (e.g dev.kasm.contoso.com). The deployment will be accessed via this zone name via https | `string` | n/a | yes |
| <a name="input_aws_key_pair"></a> [aws\_key\_pair](#input\_aws\_key\_pair) | The name of an aws keypair to use. | `string` | n/a | yes |
| <a name="input_aws_region"></a> [aws\_region](#input\_aws\_region) | The AWS Region used for deployment | `string` | `"us-east-1"` | no |
| <a name="input_aws_secret_key"></a> [aws\_secret\_key](#input\_aws\_secret\_key) | The AWS secret key used for deployment | `string` | n/a | yes |
| <a name="input_aws_ssm_iam_role_name"></a> [aws\_ssm\_iam\_role\_name](#input\_aws\_ssm\_iam\_role\_name) | The name of the SSM EC2 role to associate with Kasm VMs for SSH access | `string` | `""` | no |
| <a name="input_aws_ssm_instance_profile_name"></a> [aws\_ssm\_instance\_profile\_name](#input\_aws\_ssm\_instance\_profile\_name) | The name of the SSM EC2 Instance Profile to associate with Kasm VMs for SSH access | `string` | `""` | no |
| <a name="input_cpx_hdd_size_gb"></a> [cpx\_hdd\_size\_gb](#input\_cpx\_hdd\_size\_gb) | The HDD size in GB to configure for the Kasm cpx RDP instances | `number` | n/a | yes |
| <a name="input_cpx_instance_type"></a> [cpx\_instance\_type](#input\_cpx\_instance\_type) | The instance type for the cpxamole RDP nodes | `string` | n/a | yes |
| <a name="input_create_aws_ssm_iam_role"></a> [create\_aws\_ssm\_iam\_role](#input\_create\_aws\_ssm\_iam\_role) | Create an AWS SSM IAM role to attach to VMs for SSH/console access to VMs. | `bool` | `false` | no |
| <a name="input_database_password"></a> [database\_password](#input\_database\_password) | The password for the database. No special characters | `string` | n/a | yes |
| <a name="input_db_hdd_size_gb"></a> [db\_hdd\_size\_gb](#input\_db\_hdd\_size\_gb) | The HDD size in GB to configure for the Kasm Database instances | `number` | n/a | yes |
| <a name="input_db_instance_type"></a> [db\_instance\_type](#input\_db\_instance\_type) | The instance type for the Database | `string` | n/a | yes |
| <a name="input_ec2_ami_id"></a> [ec2\_ami\_id](#input\_ec2\_ami\_id) | The AMI used for the EC2 nodes. Recommended Ubuntu 22.04 LTS. | `string` | n/a | yes |
| <a name="input_kasm_build"></a> [kasm\_build](#input\_kasm\_build) | The URL for the Kasm Workspaces build | `string` | n/a | yes |
| <a name="input_kasm_zone_name"></a> [kasm\_zone\_name](#input\_kasm\_zone\_name) | A name given to the kasm deployment Zone | `string` | `"default"` | no |
| <a name="input_manager_token"></a> [manager\_token](#input\_manager\_token) | The manager token value for Agents to authenticate to webapps. No special characters | `string` | n/a | yes |
| <a name="input_num_agents"></a> [num\_agents](#input\_num\_agents) | The number of Agent Role Servers to create in the deployment | `number` | n/a | yes |
| <a name="input_num_cpx_nodes"></a> [num\_cpx\_nodes](#input\_num\_cpx\_nodes) | The number of Agent Role Servers to create in the deployment | `number` | n/a | yes |
| <a name="input_num_webapps"></a> [num\_webapps](#input\_num\_webapps) | The number of WebApp role servers to create in the deployment | `number` | n/a | yes |
| <a name="input_project_name"></a> [project\_name](#input\_project\_name) | The name of the deployment (e.g dev, staging). A short single word | `string` | n/a | yes |
| <a name="input_redis_password"></a> [redis\_password](#input\_redis\_password) | The password for the Redis server. No special characters | `string` | n/a | yes |
| <a name="input_service_registration_token"></a> [service\_registration\_token](#input\_service\_registration\_token) | The service registration token value for cpx RDP servers to authenticate to webapps. No special characters | `string` | n/a | yes |
| <a name="input_swap_size"></a> [swap\_size](#input\_swap\_size) | The amount of swap (in MB) to configure inside the compute instances | `number` | n/a | yes |
| <a name="input_user_password"></a> [user\_password](#input\_user\_password) | The standard (non administrator) user password. No special characters | `string` | n/a | yes |
| <a name="input_vpc_subnet_cidr"></a> [vpc\_subnet\_cidr](#input\_vpc\_subnet\_cidr) | The subnet CIDR to use for the VPC | `string` | `"10.0.0.0/16"` | no |
| <a name="input_web_access_cidrs"></a> [web\_access\_cidrs](#input\_web\_access\_cidrs) | CIDR notation of the bastion host allowed to SSH in to the machines | `list(string)` | n/a | yes |
| <a name="input_webapp_hdd_size_gb"></a> [webapp\_hdd\_size\_gb](#input\_webapp\_hdd\_size\_gb) | The HDD size in GB to configure for the Kasm WebApp instances | `number` | n/a | yes |
| <a name="input_webapp_instance_type"></a> [webapp\_instance\_type](#input\_webapp\_instance\_type) | The instance type for the webapps | `string` | n/a | yes |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_kasm_zone_settings"></a> [kasm\_zone\_settings](#output\_kasm\_zone\_settings) | Upstream Auth settings to apply to Kasm Zone configuration |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
# Detailed Terraform Deployment Diagram

View file

@ -1,23 +1,29 @@
module "standard" {
source = "./module"
aws_key_pair = var.aws_key_pair
aws_region = var.aws_region
aws_domain_name = var.aws_domain_name
project_name = var.project_name
num_agents = var.num_agents
num_webapps = var.num_webapps
num_guac_nodes = var.num_guac_nodes
vpc_subnet_cidr = var.vpc_subnet_cidr
source = "./module"
aws_key_pair = var.aws_key_pair
aws_region = var.aws_region
aws_domain_name = var.aws_domain_name
project_name = var.project_name
num_agents = var.num_agents
num_webapps = var.num_webapps
num_cpx_nodes = var.num_cpx_nodes
vpc_subnet_cidr = var.vpc_subnet_cidr
create_aws_ssm_iam_role = var.create_aws_ssm_iam_role
aws_ssm_iam_role_name = var.aws_ssm_iam_role_name
aws_ssm_instance_profile_name = var.aws_ssm_instance_profile_name
## Kasm Server settings
agent_instance_type = var.agent_instance_type
guac_instance_type = var.guac_instance_type
webapp_instance_type = var.webapp_instance_type
webapp_hdd_size_gb = var.webapp_hdd_size_gb
db_instance_type = var.db_instance_type
ec2_ami = var.ec2_ami
db_hdd_size_gb = var.db_hdd_size_gb
agent_instance_type = var.agent_instance_type
agent_hdd_size_gb = var.agent_hdd_size_gb
cpx_instance_type = var.cpx_instance_type
cpx_hdd_size_gb = var.cpx_hdd_size_gb
ec2_ami = var.ec2_ami_id
swap_size = var.swap_size
ssh_access_cidrs = var.ssh_access_cidrs
web_access_cidrs = var.web_access_cidrs
database_password = var.database_password
redis_password = var.redis_password

View file

@ -1,32 +1,32 @@
<mxfile host="65bd71144e">
<diagram id="evBy-dt-jB8v2-nSfg1o" name="AWS Multi-Server">
<mxGraphModel dx="1395" dy="1340" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="850" math="0" shadow="0">
<mxGraphModel dx="1537" dy="1071" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="850" background="#ffffff" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="15" value="WebApp Subnet 1&lt;br&gt;10.0.1.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" parent="1" vertex="1">
<mxGeometry x="531" y="189" width="130" height="141" as="geometry"/>
<mxGeometry x="461" y="199" width="130" height="141" as="geometry"/>
</mxCell>
<mxCell id="50" value="&lt;span style=&quot;color: rgb(0, 0, 0); font-size: 10px;&quot;&gt;US East&lt;br&gt;Webapp-1&lt;/span&gt;" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" parent="15" vertex="1">
<mxGeometry x="45" y="51" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="2" value="AWS Account" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_account;strokeColor=#CD2264;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#CD2264;dashed=0;" parent="1" vertex="1">
<mxGeometry x="239" y="69" width="681" height="831" as="geometry"/>
<mxGeometry x="169" y="79" width="711" height="1081" as="geometry"/>
</mxCell>
<mxCell id="11" value="Primary Region - us-east-1" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=0;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_region;strokeColor=#147EBA;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#147EBA;dashed=1;" parent="2" vertex="1">
<mxGeometry x="130" y="20" width="530" height="800" as="geometry"/>
<mxGeometry x="130" y="20" width="570" height="1050" as="geometry"/>
</mxCell>
<mxCell id="3" value="Mgmt VPC&lt;br&gt;10.0.0.0/16" style="sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_vpc;strokeColor=#879196;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#879196;dashed=0;" parent="2" vertex="1">
<mxGeometry x="171" y="49.75" width="471" height="760.25" as="geometry"/>
<mxGeometry x="171" y="49.75" width="519" height="1010.25" as="geometry"/>
</mxCell>
<mxCell id="91" value="Agent Security Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=1;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=2;" parent="2" vertex="1">
<mxGeometry x="286.5" y="461" width="150" height="156" as="geometry"/>
<mxGeometry x="286.5" y="523" width="150" height="156" as="geometry"/>
</mxCell>
<mxCell id="134" value="Guac Security Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=1;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=2;" parent="2" vertex="1">
<mxGeometry x="284" y="641" width="150" height="156" as="geometry"/>
<mxCell id="134" value="CPX Security Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=1;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=2;" parent="2" vertex="1">
<mxGeometry x="284" y="703" width="150" height="156" as="geometry"/>
</mxCell>
<mxCell id="89" value="AZ 1a" style="sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_availability_zone;strokeColor=#545B64;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#545B64;dashed=1;" parent="2" vertex="1">
<mxGeometry x="273" y="74.25" width="175" height="730.75" as="geometry"/>
<mxGeometry x="273" y="74.25" width="173" height="975.75" as="geometry"/>
</mxCell>
<mxCell id="76" value="Webapp Security&amp;nbsp; Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=0;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=3;" parent="2" vertex="1">
<mxGeometry x="285" y="102" width="330" height="167" as="geometry"/>
@ -35,16 +35,21 @@
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="4" value="Route53 Latency&lt;br style=&quot;font-size: 10px;&quot;&gt;based DNS" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#945DF2;gradientDirection=north;fillColor=#5A30B5;strokeColor=#ffffff;dashed=0;verticalLabelPosition=top;verticalAlign=bottom;align=center;html=1;fontSize=10;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.route_53;labelPosition=center;" parent="2" vertex="1">
<mxGeometry x="39" y="183" width="50" height="50" as="geometry"/>
<mxGeometry x="56" y="183" width="50" height="50" as="geometry"/>
</mxCell>
<mxCell id="126" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fillColor=#fff2cc;strokeColor=#d6b656;strokeWidth=3;" parent="2" source="5" target="108" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="5" value="Public&lt;br&gt;Web ALB" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.application_load_balancer;fillColor=#F58534;gradientColor=none;fontSize=10;" parent="2" vertex="1">
<mxCell id="5" value="Public&lt;br&gt;Web ALB" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.application_load_balancer;fillColor=#F58534;gradientColor=none;fontSize=10;fontColor=#333333;" parent="2" vertex="1">
<mxGeometry x="192" y="188" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="173" value="" style="edgeStyle=orthogonalEdgeStyle;jumpStyle=arc;jumpSize=12;html=1;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontColor=#FFFFFF;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="2" source="92" target="165">
<mxGeometry relative="1" as="geometry">
<mxPoint x="210" y="605" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="92" value="Agent Subnet 1&lt;br&gt;10.0.3.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" parent="2" vertex="1">
<mxGeometry x="290" y="478" width="140" height="130" as="geometry"/>
<mxGeometry x="290" y="540" width="140" height="130" as="geometry"/>
</mxCell>
<mxCell id="93" value="" style="group" parent="92" vertex="1" connectable="0">
<mxGeometry x="35" y="40" width="70" height="90" as="geometry"/>
@ -61,57 +66,46 @@
<mxCell id="97" value="Kasm Agents" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;" parent="93" vertex="1">
<mxGeometry y="60" width="70" height="30" as="geometry"/>
</mxCell>
<mxCell id="46" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;fontSize=10;fillColor=#d5e8d4;strokeColor=#82b366;strokeWidth=3;labelBackgroundColor=default;" parent="2" source="91" target="127" edge="1">
<mxCell id="46" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;fontSize=10;fillColor=#d5e8d4;strokeColor=#82b366;strokeWidth=3;labelBackgroundColor=default;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="2" source="91" target="127" edge="1">
<mxGeometry x="-0.4789" y="-6" relative="1" as="geometry">
<mxPoint x="-129" y="501" as="sourcePoint"/>
<Array as="points">
<mxPoint x="523" y="539"/>
<mxPoint x="523" y="601"/>
</Array>
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="25" value="Internet&lt;br&gt;Gateway" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.internet_gateway;fillColor=#F58536;gradientColor=none;fontSize=10;" parent="2" vertex="1">
<mxGeometry x="185" y="92" width="28.76" height="30" as="geometry"/>
</mxCell>
<mxCell id="16" value="WebApp Subnet 2&lt;br&gt;10.0.2.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" parent="2" vertex="1">
<mxGeometry x="475" y="120" width="130" height="140" as="geometry"/>
</mxCell>
<mxCell id="71" value="&lt;span style=&quot;color: rgb(0, 0, 0); font-size: 10px;&quot;&gt;US East&lt;br&gt;Webapp-2&lt;/span&gt;" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" parent="2" vertex="1">
<mxGeometry x="520" y="171" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="108" value="" style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;fontSize=10;fillColor=none;strokeColor=#d6b656;strokeWidth=2;dashed=1;arcSize=6;" parent="2" vertex="1">
<mxGeometry x="305" y="163" width="290" height="90" as="geometry"/>
</mxCell>
<mxCell id="120" value="DB Subnet 1&lt;br&gt;10.0.0.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" parent="2" vertex="1">
<mxGeometry x="289" y="303" width="145" height="135" as="geometry"/>
<mxGeometry x="289" y="365" width="145" height="135" as="geometry"/>
</mxCell>
<mxCell id="124" value="Kasm Database&lt;br&gt;Kasm Redis" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" parent="120" vertex="1">
<mxGeometry x="52.5" y="47.5" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="123" value="Database Security Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=1;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=4;" parent="2" vertex="1">
<mxGeometry x="286.5" y="283" width="150" height="155" as="geometry"/>
<mxGeometry x="286.5" y="345" width="150" height="165" as="geometry"/>
</mxCell>
<mxCell id="14" value="AZ 1b" style="sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_availability_zone;strokeColor=#545B64;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#545B64;dashed=1;" parent="2" vertex="1">
<mxGeometry x="462" y="73.25" width="164" height="205.75" as="geometry"/>
</mxCell>
<mxCell id="136" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;labelBackgroundColor=default;strokeWidth=3;entryX=1;entryY=0.5;entryDx=0;entryDy=0;fillColor=#d5e8d4;strokeColor=#82b366;startArrow=classic;startFill=1;endArrow=none;endFill=0;" parent="2" source="127" target="128" edge="1">
<mxCell id="136" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;labelBackgroundColor=default;strokeWidth=3;entryX=1;entryY=0.25;entryDx=0;entryDy=0;fillColor=#d5e8d4;strokeColor=#82b366;startArrow=classic;startFill=1;endArrow=none;endFill=0;" parent="2" source="127" target="128" edge="1">
<mxGeometry x="0.5668" y="8" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="137" value="&lt;span style=&quot;font-size: 10px;&quot;&gt;https://private-lb.kasm.contoso.com&lt;/span&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="136" vertex="1" connectable="0">
<mxGeometry x="-0.7987" relative="1" as="geometry">
<mxPoint y="-2" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="138" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.75;entryY=1;entryDx=0;entryDy=0;labelBackgroundColor=default;strokeWidth=3;startArrow=none;startFill=0;endArrow=classic;endFill=1;fillColor=#d5e8d4;strokeColor=#82b366;" parent="2" source="127" target="108" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="127" value="Private LB&lt;br&gt;Agent/Guac Mgmt" style="outlineConnect=0;dashed=0;verticalLabelPosition=middle;verticalAlign=middle;align=left;html=1;shape=mxgraph.aws3.application_load_balancer;fillColor=#F58534;gradientColor=none;fontSize=10;labelPosition=right;" parent="2" vertex="1">
<mxCell id="127" value="Private LB" style="outlineConnect=0;dashed=0;verticalLabelPosition=middle;verticalAlign=middle;align=left;html=1;shape=mxgraph.aws3.application_load_balancer;fillColor=#F58534;gradientColor=none;fontSize=10;labelPosition=right;fontColor=#333333;" parent="2" vertex="1">
<mxGeometry x="503" y="401" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="128" value="Guac Subnet&lt;br&gt;10.0.4.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" parent="2" vertex="1">
<mxGeometry x="287.5" y="658" width="140" height="130" as="geometry"/>
<mxCell id="128" value="CPX Subnet&lt;br&gt;10.0.4.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" parent="2" vertex="1">
<mxGeometry x="287.5" y="720" width="140" height="130" as="geometry"/>
</mxCell>
<mxCell id="129" value="" style="group" parent="128" vertex="1" connectable="0">
<mxGeometry x="35" y="40" width="70" height="90" as="geometry"/>
@ -125,39 +119,109 @@
<mxCell id="132" value="" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;gradientColor=#F78E04;gradientDirection=north;fillColor=#D05C17;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.ec2;" parent="129" vertex="1">
<mxGeometry x="25" y="20" width="40" height="40" as="geometry"/>
</mxCell>
<mxCell id="133" value="Kasm Guac RDP" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;" parent="129" vertex="1">
<mxCell id="133" value="Kasm cpx RDP" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;" parent="129" vertex="1">
<mxGeometry y="60" width="70" height="30" as="geometry"/>
</mxCell>
<mxCell id="102" value="Load Balancer&lt;br style=&quot;font-size: 10px;&quot;&gt;Access Logs S3" style="sketch=0;outlineConnect=0;fontColor=#232F3E;gradientColor=none;fillColor=#3F8624;strokeColor=none;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=10;fontStyle=0;aspect=fixed;pointerEvents=1;shape=mxgraph.aws4.bucket;labelBackgroundColor=none;" parent="2" vertex="1">
<mxGeometry x="588" y="748" width="28.85" height="30" as="geometry"/>
<mxCell id="102" value="Load Balancer&lt;br style=&quot;font-size: 10px;&quot;&gt;Access Logs S3" style="sketch=0;outlineConnect=0;fontColor=#333333;gradientColor=none;fillColor=#3F8624;strokeColor=none;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=10;fontStyle=0;aspect=fixed;pointerEvents=1;shape=mxgraph.aws4.bucket;labelBackgroundColor=none;" parent="2" vertex="1">
<mxGeometry x="638" y="997" width="28.85" height="30" as="geometry"/>
</mxCell>
<mxCell id="139" value="NAT GW" style="outlineConnect=0;dashed=0;verticalLabelPosition=middle;verticalAlign=middle;align=left;html=1;shape=mxgraph.aws3.vpc_nat_gateway;fillColor=#F58536;gradientColor=none;labelPosition=right;" vertex="1" parent="2">
<mxGeometry x="180" y="608" width="40" height="41.73" as="geometry"/>
<mxCell id="148" value="Windows Security Group" style="fontStyle=0;verticalAlign=top;align=center;spacingTop=-2;fillColor=none;rounded=1;whiteSpace=wrap;html=1;strokeColor=#FF0000;strokeWidth=2;dashed=1;container=1;collapsible=0;expand=0;recursiveResize=0;labelBackgroundColor=none;fontSize=10;fontColor=#000000;arcSize=2;" vertex="1" parent="2">
<mxGeometry x="285" y="882" width="150" height="156" as="geometry"/>
</mxCell>
<mxCell id="144" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;strokeWidth=3;fillColor=#f8cecc;strokeColor=#b85450;" edge="1" parent="2" source="94" target="139">
<mxCell id="149" value="Windows Subnet&lt;br&gt;10.0.5.0/24" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_security_group;grStroke=0;strokeColor=#248814;fillColor=#E9F3E6;verticalAlign=top;align=left;spacingLeft=30;fontColor=#248814;dashed=0;" vertex="1" parent="2">
<mxGeometry x="290" y="900" width="140" height="130" as="geometry"/>
</mxCell>
<mxCell id="155" value="This Terraform does not deploy Windows VMs, only subnets and security groups" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontColor=#000000;" vertex="1" parent="149">
<mxGeometry y="43" width="140" height="80" as="geometry"/>
</mxCell>
<mxCell id="156" value="" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;labelBackgroundColor=default;strokeWidth=3;fillColor=#d5e8d4;strokeColor=#82b366;startArrow=classic;startFill=1;endArrow=none;endFill=0;entryX=1;entryY=0.25;entryDx=0;entryDy=0;" edge="1" parent="2" source="127" target="155">
<mxGeometry x="0.5668" y="8" relative="1" as="geometry">
<mxPoint as="offset"/>
<mxPoint x="533" y="451" as="sourcePoint"/>
<mxPoint x="481" y="1061" as="targetPoint"/>
<Array as="points">
<mxPoint x="523" y="961"/>
<mxPoint x="430" y="961"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="157" value="&lt;span style=&quot;font-size: 10px;&quot;&gt;https://private-lb.kasm.contoso.com&lt;/span&gt;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];labelBackgroundColor=#FFFFFF;fontColor=#333333;" vertex="1" connectable="0" parent="156">
<mxGeometry x="-0.7987" relative="1" as="geometry">
<mxPoint x="8" y="4" as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="159" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontColor=#FFFFFF;jumpStyle=arc;jumpSize=12;" edge="1" parent="2" source="108" target="147">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="377" y="311"/>
<mxPoint x="471" y="311"/>
<mxPoint x="471" y="356"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="162" style="edgeStyle=orthogonalEdgeStyle;jumpStyle=arc;jumpSize=12;html=1;exitX=0.863;exitY=1.002;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontColor=#FFFFFF;exitPerimeter=0;" edge="1" parent="2" source="108" target="147">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="145" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;strokeWidth=3;fillColor=#f8cecc;strokeColor=#b85450;" edge="1" parent="2" source="130" target="139">
<mxCell id="108" value="" style="rounded=1;whiteSpace=wrap;html=1;labelBackgroundColor=none;fontSize=10;fillColor=none;strokeColor=#d6b656;strokeWidth=2;dashed=1;arcSize=6;" parent="2" vertex="1">
<mxGeometry x="305" y="163" width="290" height="90" as="geometry"/>
</mxCell>
<mxCell id="147" value="NAT GW" style="outlineConnect=0;dashed=0;verticalLabelPosition=top;verticalAlign=bottom;align=center;html=1;shape=mxgraph.aws3.vpc_nat_gateway;fillColor=#F58536;gradientColor=none;labelPosition=center;fontColor=#333333;" vertex="1" parent="2">
<mxGeometry x="615" y="335" width="40" height="41.73" as="geometry"/>
</mxCell>
<mxCell id="163" style="edgeStyle=orthogonalEdgeStyle;jumpStyle=arc;jumpSize=12;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontColor=#FFFFFF;" edge="1" parent="2" source="123" target="147">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="471" y="428"/>
<mxPoint x="471" y="356"/>
</Array>
</mxGeometry>
</mxCell>
<mxCell id="164" style="edgeStyle=orthogonalEdgeStyle;jumpStyle=arc;jumpSize=12;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontColor=#FFFFFF;" edge="1" parent="2" target="147">
<mxGeometry relative="1" as="geometry">
<mxPoint x="431" y="804" as="sourcePoint"/>
</mxGeometry>
</mxCell>
<mxCell id="165" value="Internet&lt;br&gt;Gateway" style="outlineConnect=0;dashed=0;verticalLabelPosition=middle;verticalAlign=middle;align=left;html=1;shape=mxgraph.aws3.internet_gateway;fillColor=#F58536;gradientColor=none;fontSize=10;fontColor=#333333;labelPosition=right;" vertex="1" parent="2">
<mxGeometry x="181" y="765" width="39" height="40" as="geometry"/>
</mxCell>
<mxCell id="166" style="edgeStyle=orthogonalEdgeStyle;jumpStyle=arc;jumpSize=12;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontColor=#FFFFFF;" edge="1" parent="2" source="155" target="165">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="44" value="Management/User&lt;br&gt;Access Traffic" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.71;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;fontSize=10;strokeWidth=3;" parent="1" source="12" target="4" edge="1">
<mxCell id="44" value="Management/User&lt;br&gt;Access Traffic" style="edgeStyle=orthogonalEdgeStyle;html=1;exitX=1;exitY=0.71;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;fontSize=10;strokeWidth=3;strokeColor=#333333;labelBackgroundColor=none;fontColor=#1A1A1A;" parent="1" source="12" target="4" edge="1">
<mxGeometry x="-0.2779" y="15" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="12" value="&lt;font style=&quot;font-size: 10px;&quot;&gt;https://kasm.contoso.com&lt;/font&gt;" style="points=[[0.35,0,0],[0.98,0.51,0],[1,0.71,0],[0.67,1,0],[0,0.795,0],[0,0.65,0]];verticalLabelPosition=top;sketch=0;html=1;verticalAlign=bottom;aspect=fixed;align=center;pointerEvents=1;shape=mxgraph.cisco19.user;fillColor=#005073;strokeColor=none;fontColor=#005073;labelPosition=center;" parent="1" vertex="1">
<mxGeometry x="100" y="227" width="70" height="70" as="geometry"/>
<mxGeometry x="30" y="237" width="70" height="70" as="geometry"/>
</mxCell>
<mxCell id="119" value="&lt;u style=&quot;font-size: 16px;&quot;&gt;AWS Multi-Server (Single Region with NAT Gateway) Terraform Deployment&lt;/u&gt;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;dashed=1;labelBackgroundColor=none;strokeWidth=2;fontSize=16;fontColor=#000000;" parent="1" vertex="1">
<mxGeometry x="275" y="20" width="551" height="30" as="geometry"/>
<mxGeometry x="205" y="30" width="551" height="30" as="geometry"/>
</mxCell>
<mxCell id="141" value="" style="aspect=fixed;perimeter=ellipsePerimeter;html=1;align=center;shadow=0;dashed=0;spacingTop=3;image;image=img/lib/active_directory/internet_cloud.svg;" vertex="1" parent="1">
<mxGeometry x="86" y="658" width="126.98" height="80" as="geometry"/>
<mxCell id="160" value="" style="group" vertex="1" connectable="0" parent="1">
<mxGeometry x="940" y="395" width="126.98000000000002" height="80" as="geometry"/>
</mxCell>
<mxCell id="142" value="Kasm Workspace Internet" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="110" y="680" width="90" height="30" as="geometry"/>
<mxCell id="145" value="" style="aspect=fixed;perimeter=ellipsePerimeter;html=1;align=center;shadow=0;dashed=0;spacingTop=3;image;image=img/lib/active_directory/internet_cloud.svg;" vertex="1" parent="160">
<mxGeometry width="126.98" height="80" as="geometry"/>
</mxCell>
<mxCell id="143" style="edgeStyle=none;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;fillColor=#f8cecc;strokeColor=#b85450;" edge="1" parent="1" source="139" target="141">
<mxCell id="146" value="Internet" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontColor=#000000;" vertex="1" parent="160">
<mxGeometry x="24" y="22" width="90" height="30" as="geometry"/>
</mxCell>
<mxCell id="174" value="kkkkkkk" style="group" vertex="1" connectable="0" parent="1">
<mxGeometry x="23" y="824" width="126.98000000000002" height="80" as="geometry"/>
</mxCell>
<mxCell id="175" value="" style="aspect=fixed;perimeter=ellipsePerimeter;html=1;align=center;shadow=0;dashed=0;spacingTop=3;image;image=img/lib/active_directory/internet_cloud.svg;" vertex="1" parent="174">
<mxGeometry width="126.98" height="80" as="geometry"/>
</mxCell>
<mxCell id="176" value="Internet" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontColor=#000000;" vertex="1" parent="174">
<mxGeometry x="24" y="22" width="90" height="30" as="geometry"/>
</mxCell>
<mxCell id="177" style="edgeStyle=orthogonalEdgeStyle;jumpStyle=arc;jumpSize=12;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontColor=#FFFFFF;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="165" target="175">
<mxGeometry relative="1" as="geometry">
<mxPoint x="130" y="769.9999999999998" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="178" style="edgeStyle=orthogonalEdgeStyle;jumpStyle=arc;jumpSize=12;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;labelBackgroundColor=none;strokeColor=#7A0000;strokeWidth=3;fontColor=#FFFFFF;" edge="1" parent="1" source="147" target="145">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
</root>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 218 KiB

Before After
Before After

View file

@ -0,0 +1,148 @@
# module
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | ~> 5.0 |
## Providers
| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | 5.36.0 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [aws_acm_certificate.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate) | resource |
| [aws_acm_certificate_validation.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation) | resource |
| [aws_eip.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip) | resource |
| [aws_iam_instance_profile.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_instance_profile) | resource |
| [aws_iam_role.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws_iam_role_policy_attachment.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws_instance.agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource |
| [aws_instance.cpx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource |
| [aws_instance.db](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource |
| [aws_instance.webapp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance) | resource |
| [aws_internet_gateway.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/internet_gateway) | resource |
| [aws_lb.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb) | resource |
| [aws_lb.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb) | resource |
| [aws_lb_listener.http](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener) | resource |
| [aws_lb_listener.https](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener) | resource |
| [aws_lb_listener.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener) | resource |
| [aws_lb_target_group.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_target_group) | resource |
| [aws_lb_target_group.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_target_group) | resource |
| [aws_lb_target_group_attachment.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_target_group_attachment) | resource |
| [aws_lb_target_group_attachment.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_target_group_attachment) | resource |
| [aws_nat_gateway.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/nat_gateway) | resource |
| [aws_route53_health_check.kasm-elb-hc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_health_check) | resource |
| [aws_route53_record.private](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_route53_record.public](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_route53_record.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_route_table.ig](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource |
| [aws_route_table.nat](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource |
| [aws_route_table_association.agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_route_table_association.alb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_route_table_association.cpx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_route_table_association.db](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_route_table_association.webapp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_route_table_association.windows](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_s3_bucket.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
| [aws_s3_bucket_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
| [aws_s3_bucket_public_access_block.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
| [aws_s3_bucket_server_side_encryption_configuration.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource |
| [aws_security_group.agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.cpx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.db](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.private_lb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.public_lb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.webapp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group.windows](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_security_group_rule.agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.agent_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.cpx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.cpx_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.db](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.db_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.private_lb_agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.private_lb_cpx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.private_lb_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.private_lb_windows](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.public_lb_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.public_lb_ingress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.webapp_agent_ingress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.webapp_cpx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.webapp_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.webapp_private_lb_ingress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.webapp_public_lb_ingress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.webapp_windows](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.windows](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_security_group_rule.windows_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource |
| [aws_subnet.agent](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_subnet.alb](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_subnet.cpx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_subnet.db](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_subnet.webapp](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_subnet.windows](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_vpc.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc) | resource |
| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source |
| [aws_elb_service_account.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/elb_service_account) | data source |
| [aws_iam_policy_document.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_route53_zone.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route53_zone) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_admin_password"></a> [admin\_password](#input\_admin\_password) | The administrative user password. No special characters | `string` | n/a | yes |
| <a name="input_agent_hdd_size_gb"></a> [agent\_hdd\_size\_gb](#input\_agent\_hdd\_size\_gb) | The HDD size for Kasm Agent nodes | `number` | n/a | yes |
| <a name="input_agent_instance_type"></a> [agent\_instance\_type](#input\_agent\_instance\_type) | The instance type for the Agents | `string` | `"t3.medium"` | no |
| <a name="input_agent_security_rules"></a> [agent\_security\_rules](#input\_agent\_security\_rules) | A map of objects of security rules to apply to the Kasm WebApp server | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> }))</pre> | <pre>{<br> "https": {<br> "from_port": 443,<br> "protocol": "tcp",<br> "to_port": 443<br> }<br>}</pre> | no |
| <a name="input_anywhere"></a> [anywhere](#input\_anywhere) | Anywhere route subnet | `string` | `"0.0.0.0/0"` | no |
| <a name="input_aws_domain_name"></a> [aws\_domain\_name](#input\_aws\_domain\_name) | The Route53 Zone used for the dns entries. This must already exist in the AWS account. (e.g dev.kasm.contoso.com). The deployment will be accessed via this zone name via https | `string` | n/a | yes |
| <a name="input_aws_key_pair"></a> [aws\_key\_pair](#input\_aws\_key\_pair) | The name of an aws keypair to use. | `string` | n/a | yes |
| <a name="input_aws_region"></a> [aws\_region](#input\_aws\_region) | The AWS region for the deployment. (e.g us-east-1) | `string` | n/a | yes |
| <a name="input_aws_ssm_iam_role_name"></a> [aws\_ssm\_iam\_role\_name](#input\_aws\_ssm\_iam\_role\_name) | The name of the SSM EC2 role to associate with Kasm VMs for SSH access | `string` | `""` | no |
| <a name="input_aws_ssm_instance_profile_name"></a> [aws\_ssm\_instance\_profile\_name](#input\_aws\_ssm\_instance\_profile\_name) | The name of the SSM EC2 Instance Profile to associate with Kasm VMs for SSH access | `string` | `""` | no |
| <a name="input_cpx_hdd_size_gb"></a> [cpx\_hdd\_size\_gb](#input\_cpx\_hdd\_size\_gb) | The HDD size for Kasm Guac RDP nodes | `number` | n/a | yes |
| <a name="input_cpx_instance_type"></a> [cpx\_instance\_type](#input\_cpx\_instance\_type) | The instance type for the cpxamole RDP nodes | `string` | `"t3.medium"` | no |
| <a name="input_cpx_security_rules"></a> [cpx\_security\_rules](#input\_cpx\_security\_rules) | A map of objects of security rules to apply to the Kasm Connection Proxy server | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> }))</pre> | <pre>{<br> "https": {<br> "from_port": 443,<br> "protocol": "tcp",<br> "to_port": 443<br> }<br>}</pre> | no |
| <a name="input_create_aws_ssm_iam_role"></a> [create\_aws\_ssm\_iam\_role](#input\_create\_aws\_ssm\_iam\_role) | Create an AWS SSM IAM role to attach to VMs for SSH/console access to VMs. | `bool` | `false` | no |
| <a name="input_database_password"></a> [database\_password](#input\_database\_password) | The password for the database. No special characters | `string` | n/a | yes |
| <a name="input_db_hdd_size_gb"></a> [db\_hdd\_size\_gb](#input\_db\_hdd\_size\_gb) | The HDD size for Kasm DB | `number` | n/a | yes |
| <a name="input_db_instance_type"></a> [db\_instance\_type](#input\_db\_instance\_type) | The instance type for the Database | `string` | `"t3.small"` | no |
| <a name="input_db_security_rules"></a> [db\_security\_rules](#input\_db\_security\_rules) | A map of objects of security rules to apply to the Kasm DB | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> }))</pre> | <pre>{<br> "postgres": {<br> "from_port": 5432,<br> "protocol": "tcp",<br> "to_port": 5432<br> },<br> "redis": {<br> "from_port": 6379,<br> "protocol": "tcp",<br> "to_port": 6379<br> }<br>}</pre> | no |
| <a name="input_default_egress"></a> [default\_egress](#input\_default\_egress) | Default egress security rule for all security groups | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> cidr_subnets = list(string)<br> }))</pre> | <pre>{<br> "all": {<br> "cidr_subnets": [<br> "0.0.0.0/0"<br> ],<br> "from_port": 0,<br> "protocol": "-1",<br> "to_port": 0<br> }<br>}</pre> | no |
| <a name="input_ec2_ami"></a> [ec2\_ami](#input\_ec2\_ami) | The AMI used for the EC2 nodes. Recommended Ubuntu 20.04 LTS. | `string` | n/a | yes |
| <a name="input_kasm_build"></a> [kasm\_build](#input\_kasm\_build) | The URL for the Kasm Workspaces build | `string` | n/a | yes |
| <a name="input_kasm_zone_name"></a> [kasm\_zone\_name](#input\_kasm\_zone\_name) | A name given to the kasm deployment Zone | `string` | `"default"` | no |
| <a name="input_manager_token"></a> [manager\_token](#input\_manager\_token) | The manager token value for Agents to authenticate to webapps. No special characters | `string` | n/a | yes |
| <a name="input_num_agents"></a> [num\_agents](#input\_num\_agents) | The number of Agent Role Servers to create in the deployment | `number` | `2` | no |
| <a name="input_num_cpx_nodes"></a> [num\_cpx\_nodes](#input\_num\_cpx\_nodes) | The number of cpx RDP Role Servers to create in the deployment | `number` | `2` | no |
| <a name="input_num_webapps"></a> [num\_webapps](#input\_num\_webapps) | The number of WebApp role servers to create in the deployment | `number` | `2` | no |
| <a name="input_private_lb_security_rules"></a> [private\_lb\_security\_rules](#input\_private\_lb\_security\_rules) | A map of objects of security rules to apply to the Private ALB | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> }))</pre> | <pre>{<br> "https": {<br> "from_port": 443,<br> "protocol": "tcp",<br> "to_port": 443<br> }<br>}</pre> | no |
| <a name="input_project_name"></a> [project\_name](#input\_project\_name) | The name of the deployment (e.g dev, staging). A short single word | `string` | n/a | yes |
| <a name="input_public_lb_security_rules"></a> [public\_lb\_security\_rules](#input\_public\_lb\_security\_rules) | A map of objects of security rules to apply to the Public ALB | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> }))</pre> | <pre>{<br> "http": {<br> "from_port": 80,<br> "protocol": "tcp",<br> "to_port": 80<br> },<br> "https": {<br> "from_port": 443,<br> "protocol": "tcp",<br> "to_port": 443<br> }<br>}</pre> | no |
| <a name="input_redis_password"></a> [redis\_password](#input\_redis\_password) | The password for the Redis server. No special characters | `string` | n/a | yes |
| <a name="input_service_registration_token"></a> [service\_registration\_token](#input\_service\_registration\_token) | The service registration token value for cpx RDP servers to authenticate to webapps. No special characters | `string` | n/a | yes |
| <a name="input_swap_size"></a> [swap\_size](#input\_swap\_size) | The amount of swap (in MB) to configure inside the compute instances | `number` | n/a | yes |
| <a name="input_user_password"></a> [user\_password](#input\_user\_password) | The standard (non administrator) user password. No special characters | `string` | n/a | yes |
| <a name="input_vpc_subnet_cidr"></a> [vpc\_subnet\_cidr](#input\_vpc\_subnet\_cidr) | The subnet CIDR to use for the VPC | `string` | `"10.0.0.0/16"` | no |
| <a name="input_web_access_cidrs"></a> [web\_access\_cidrs](#input\_web\_access\_cidrs) | CIDR notation of the bastion host allowed to SSH in to the machines | `list(string)` | <pre>[<br> "0.0.0.0/0"<br>]</pre> | no |
| <a name="input_webapp_hdd_size_gb"></a> [webapp\_hdd\_size\_gb](#input\_webapp\_hdd\_size\_gb) | The HDD size for Kasm Webapp nodes | `number` | n/a | yes |
| <a name="input_webapp_instance_type"></a> [webapp\_instance\_type](#input\_webapp\_instance\_type) | The instance type for the webapps | `string` | `"t3.small"` | no |
| <a name="input_webapp_security_rules"></a> [webapp\_security\_rules](#input\_webapp\_security\_rules) | A map of objects of security rules to apply to the Kasm WebApp server | <pre>object({<br> from_port = number<br> to_port = number<br> protocol = string<br> })</pre> | <pre>{<br> "from_port": 443,<br> "protocol": "tcp",<br> "to_port": 443<br>}</pre> | no |
| <a name="input_windows_security_rules"></a> [windows\_security\_rules](#input\_windows\_security\_rules) | A map of objects of security rules to apply to the Kasm Windows VMs | <pre>map(object({<br> from_port = number<br> to_port = number<br> protocol = string<br> }))</pre> | <pre>{<br> "cpx_api": {<br> "from_port": 4902,<br> "protocol": "tcp",<br> "to_port": 4902<br> },<br> "cpx_rdp": {<br> "from_port": 3389,<br> "protocol": "tcp",<br> "to_port": 3389<br> },<br> "webapp_api": {<br> "from_port": 4902,<br> "protocol": "tcp",<br> "to_port": 4902<br> }<br>}</pre> | no |
## Outputs
No outputs.
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

View file

@ -1,14 +1,16 @@
resource "aws_instance" "kasm-agent" {
count = var.num_agents
resource "aws_instance" "agent" {
count = var.num_agents
ami = var.ec2_ami
instance_type = var.agent_instance_type
vpc_security_group_ids = [data.aws_security_group.data-kasm_agent_sg.id]
subnet_id = data.aws_subnet.data-kasm_agent_subnet.id
vpc_security_group_ids = [aws_security_group.agent.id]
subnet_id = aws_subnet.agent.id
key_name = var.aws_key_pair
associate_public_ip_address = false
iam_instance_profile = one(aws_iam_instance_profile.this[*].id)
associate_public_ip_address = true
root_block_device {
volume_size = 120
volume_size = var.agent_hdd_size_gb
}
user_data = templatefile("${path.module}/userdata/agent_bootstrap.sh",
@ -20,7 +22,14 @@ resource "aws_instance" "kasm-agent" {
}
)
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
instance_metadata_tags = null
}
tags = {
Name = "${var.project_name}-${var.kasm_zone_name}-kasm-agent"
Name = "${var.project_name}-${var.kasm_zone_name}-kasm-agent-${count.index}"
}
}

View file

@ -1,60 +0,0 @@
data "aws_route53_zone" "kasm-route53-zone" {
name = var.aws_domain_name
}
data "aws_elb_service_account" "main" {}
resource "aws_s3_bucket" "kasm_s3_logs" {
bucket_prefix = "${var.project_name}-${var.kasm_zone_name}-"
force_destroy = true
}
resource "aws_s3_bucket_acl" "kasm_s3_acl" {
bucket = aws_s3_bucket.kasm_s3_logs.id
acl = "private"
}
resource "aws_s3_bucket_policy" "kasm_s3_logs_policy" {
bucket = aws_s3_bucket.kasm_s3_logs.id
policy = jsonencode({
Id = "Policy"
Version = "2012-10-17"
Statement = [
{
Action = [
"s3:PutObject"
]
Effect = "Allow"
Resource = "${aws_s3_bucket.kasm_s3_logs.arn}/AWSLogs/*"
Principal = {
AWS = [
data.aws_elb_service_account.main.arn
]
}
}
]
})
}
resource "aws_s3_bucket_server_side_encryption_configuration" "encrypt_elb_bucket" {
bucket = aws_s3_bucket.kasm_s3_logs.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
data "aws_s3_bucket" "data-kasm_s3_logs_bucket" {
bucket = aws_s3_bucket.kasm_s3_logs.bucket
}
resource "aws_s3_bucket_public_access_block" "s3_log_public_access" {
bucket = aws_s3_bucket.kasm_s3_logs.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}

View file

@ -1,3 +0,0 @@
data "aws_availability_zones" "available" {
state = "available"
}

View file

@ -1,4 +1,4 @@
resource "aws_acm_certificate" "kasm-alb-cert" {
resource "aws_acm_certificate" "this" {
domain_name = var.aws_domain_name
subject_alternative_names = ["*.${var.aws_domain_name}"]
validation_method = "DNS"
@ -8,9 +8,9 @@ resource "aws_acm_certificate" "kasm-alb-cert" {
}
}
resource "aws_route53_record" "kasm-route53-cert-validation-record" {
resource "aws_route53_record" "this" {
for_each = {
for dvo in aws_acm_certificate.kasm-alb-cert.domain_validation_options : dvo.domain_name => {
for dvo in aws_acm_certificate.this.domain_validation_options : dvo.domain_name => {
name = dvo.resource_record_name
record = dvo.resource_record_value
type = dvo.resource_record_type
@ -19,13 +19,13 @@ resource "aws_route53_record" "kasm-route53-cert-validation-record" {
name = each.value.name
type = each.value.type
records = [each.value.record]
zone_id = data.aws_route53_zone.kasm-route53-zone.id
zone_id = data.aws_route53_zone.this.id
ttl = 30
allow_overwrite = true
}
resource "aws_acm_certificate_validation" "kasm-elb-certificate-validation" {
certificate_arn = aws_acm_certificate.kasm-alb-cert.arn
validation_record_fqdns = [for record in aws_route53_record.kasm-route53-cert-validation-record : record.fqdn]
resource "aws_acm_certificate_validation" "this" {
certificate_arn = aws_acm_certificate.this.arn
validation_record_fqdns = [for record in aws_route53_record.this : record.fqdn]
}

View file

@ -1,12 +1,13 @@
resource "aws_instance" "kasm-db" {
resource "aws_instance" "db" {
ami = var.ec2_ami
instance_type = var.db_instance_type
vpc_security_group_ids = [data.aws_security_group.data-kasm_db_sg.id]
subnet_id = data.aws_subnet.data-kasm_db_subnet.id
vpc_security_group_ids = [aws_security_group.db.id]
subnet_id = aws_subnet.db.id
key_name = var.aws_key_pair
iam_instance_profile = one(aws_iam_instance_profile.this[*].id)
root_block_device {
volume_size = 40
volume_size = var.db_hdd_size_gb
}
user_data = templatefile("${path.module}/userdata/db_bootstrap.sh",
@ -22,11 +23,14 @@ resource "aws_instance" "kasm-db" {
}
)
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
instance_metadata_tags = null
}
tags = {
Name = "${var.project_name}-kasm-db"
}
}
data "aws_instance" "data-kasm_db" {
instance_id = aws_instance.kasm-db.id
}

View file

@ -0,0 +1,14 @@
locals {
private_lb_hostname = "${var.aws_region}-private.${var.aws_domain_name}"
}
data "aws_availability_zones" "available" {
state = "available"
}
data "aws_route53_zone" "this" {
name = var.aws_domain_name
}
data "aws_elb_service_account" "main" {}

View file

@ -0,0 +1,45 @@
resource "aws_s3_bucket" "this" {
bucket_prefix = "${var.project_name}-${var.kasm_zone_name}-"
force_destroy = true
}
resource "aws_s3_bucket_policy" "this" {
bucket = aws_s3_bucket.this.id
policy = jsonencode({
Id = "Policy"
Version = "2012-10-17"
Statement = [
{
Action = [
"s3:PutObject"
]
Effect = "Allow"
Resource = "${aws_s3_bucket.this.arn}/AWSLogs/*"
Principal = {
AWS = [
data.aws_elb_service_account.main.arn
]
}
}
]
})
}
resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
bucket = aws_s3_bucket.this.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
resource "aws_s3_bucket_public_access_block" "this" {
bucket = aws_s3_bucket.this.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}

View file

@ -1,17 +1,18 @@
resource "aws_instance" "kasm-guac" {
count = var.num_guac_nodes
ami = var.ec2_ami
instance_type = var.guac_instance_type
vpc_security_group_ids = [data.aws_security_group.data-kasm_guac_sg.id]
subnet_id = data.aws_subnet.data-kasm_guac_subnet.id
key_name = var.aws_key_pair
associate_public_ip_address = false
resource "aws_instance" "cpx" {
count = var.num_cpx_nodes
ami = var.ec2_ami
instance_type = var.cpx_instance_type
vpc_security_group_ids = aws_security_group.cpx[*].id
subnet_id = one(aws_subnet.cpx[*].id)
key_name = var.aws_key_pair
iam_instance_profile = one(aws_iam_instance_profile.this[*].id)
root_block_device {
volume_size = 120
volume_size = var.cpx_hdd_size_gb
}
user_data = templatefile("${path.module}/userdata/guac_bootstrap.sh",
user_data = templatefile("${path.module}/userdata/cpx_bootstrap.sh",
{
kasm_build_url = var.kasm_build
swap_size = var.swap_size
@ -20,7 +21,14 @@ resource "aws_instance" "kasm-guac" {
}
)
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
instance_metadata_tags = null
}
tags = {
Name = "${var.project_name}-${var.kasm_zone_name}-kasm-guac"
Name = "${var.project_name}-${var.kasm_zone_name}-kasm-cpx-${count.index}"
}
}

View file

@ -1,14 +0,0 @@
resource "aws_eip" "nat_gateway_eip" {
vpc = true
}
resource "aws_nat_gateway" "agent_and_guac_natgw" {
allocation_id = aws_eip.nat_gateway_eip.id
subnet_id = data.aws_subnet.data-kasm_webapp_subnets[0].id
depends_on = [data.aws_internet_gateway.data-kasm-default-ig]
}
data "aws_nat_gateway" "data-agent_and_guac_natgw" {
id = aws_nat_gateway.agent_and_guac_natgw.id
}

View file

@ -1,16 +1,12 @@
locals {
private_lb_hostname = "${var.kasm_zone_name}-private-lb.${var.aws_domain_name}"
}
resource "aws_lb" "kasm-private-alb" {
resource "aws_lb" "private" {
name = "${var.project_name}-private-lb"
internal = true
load_balancer_type = "application"
security_groups = [data.aws_security_group.data-kasm_default_elb_sg.id]
subnets = data.aws_subnet.data-kasm_webapp_subnets[*].id
security_groups = [aws_security_group.private_lb.id]
subnets = aws_subnet.webapp[*].id
access_logs {
bucket = data.aws_s3_bucket.data-kasm_s3_logs_bucket.bucket
bucket = aws_s3_bucket.this.bucket
enabled = true
}
@ -19,15 +15,11 @@ resource "aws_lb" "kasm-private-alb" {
}
}
data "aws_lb" "data-kasm_private_alb" {
arn = aws_lb.kasm-private-alb.arn
}
resource "aws_lb_target_group" "kasm-private-target-group" {
resource "aws_lb_target_group" "private" {
name = "${var.project_name}-private-target-group"
port = 443
protocol = "HTTPS"
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
vpc_id = aws_vpc.this.id
health_check {
path = "/api/__healthcheck"
@ -40,19 +32,15 @@ resource "aws_lb_target_group" "kasm-private-target-group" {
}
}
data "aws_lb_target_group" "data-kasm_private_target_group" {
arn = aws_lb_target_group.kasm-private-target-group.arn
}
resource "aws_lb_listener" "kasm-private-alb-listener" {
load_balancer_arn = data.aws_lb.data-kasm_private_alb.arn
resource "aws_lb_listener" "private" {
load_balancer_arn = aws_lb.private.arn
port = "443"
protocol = "HTTPS"
certificate_arn = aws_acm_certificate_validation.kasm-elb-certificate-validation.certificate_arn
certificate_arn = aws_acm_certificate_validation.this.certificate_arn
default_action {
type = "forward"
target_group_arn = data.aws_lb_target_group.data-kasm_private_target_group.arn
target_group_arn = aws_lb_target_group.private.arn
}
tags = {
@ -60,34 +48,22 @@ resource "aws_lb_listener" "kasm-private-alb-listener" {
}
}
resource "aws_lb_target_group_attachment" "kasm-private-target-group-attachment" {
count = var.num_webapps
target_group_arn = data.aws_lb_target_group.data-kasm_private_target_group.arn
target_id = data.aws_instance.data-kasm_web_app[count.index].id
resource "aws_lb_target_group_attachment" "private" {
count = var.num_webapps
target_group_arn = aws_lb_target_group.private.arn
target_id = aws_instance.webapp[count.index].id
port = 443
}
resource "aws_route53_record" "kasm-route53-private-elb-record" {
zone_id = data.aws_route53_zone.kasm-route53-zone.zone_id
resource "aws_route53_record" "private" {
zone_id = data.aws_route53_zone.this.zone_id
name = local.private_lb_hostname
type = "A"
alias {
name = data.aws_lb.data-kasm_private_alb.dns_name
zone_id = data.aws_lb.data-kasm_private_alb.zone_id
name = aws_lb.private.dns_name
zone_id = aws_lb.private.zone_id
evaluate_target_health = true
}
}
resource "aws_route53_health_check" "kasm-private-elb-hc" {
fqdn = local.private_lb_hostname
port = 443
type = "HTTPS"
resource_path = "/api/__healthcheck"
failure_threshold = "5"
request_interval = "30"
tags = {
Name = "hc-${var.kasm_zone_name}-private-lb.${var.aws_domain_name}"
}
}

View file

@ -1,8 +1,10 @@
terraform {
required_version = "~> 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
#version = "4.56.0"
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}

View file

@ -1,12 +1,12 @@
resource "aws_lb" "kasm-alb" {
resource "aws_lb" "public" {
name = "${var.project_name}-lb"
internal = false
load_balancer_type = "application"
security_groups = [data.aws_security_group.data-kasm_default_elb_sg.id]
subnets = data.aws_subnet.data-kasm_webapp_subnets[*].id
security_groups = [aws_security_group.public_lb.id]
subnets = aws_subnet.alb[*].id
access_logs {
bucket = data.aws_s3_bucket.data-kasm_s3_logs_bucket.bucket
bucket = aws_s3_bucket.this.bucket
enabled = true
}
@ -15,40 +15,15 @@ resource "aws_lb" "kasm-alb" {
}
}
data "aws_lb" "data-kasm_alb" {
arn = aws_lb.kasm-alb.arn
}
resource "aws_lb_target_group" "kasm-target-group" {
name = "${var.project_name}-target-group"
port = 443
protocol = "HTTPS"
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
health_check {
path = "/api/__healthcheck"
matcher = 200
protocol = "HTTPS"
}
tags = {
Name = "${var.project_name}-kasm-public-tg"
}
}
data "aws_lb_target_group" "data-kasm_target_group" {
arn = aws_lb_target_group.kasm-target-group.arn
}
resource "aws_lb_listener" "kasm-alb-listener" {
load_balancer_arn = data.aws_lb.data-kasm_alb.arn
resource "aws_lb_listener" "https" {
load_balancer_arn = aws_lb.public.arn
port = "443"
protocol = "HTTPS"
certificate_arn = aws_acm_certificate_validation.kasm-elb-certificate-validation.certificate_arn
certificate_arn = aws_acm_certificate_validation.this.certificate_arn
default_action {
type = "forward"
target_group_arn = data.aws_lb_target_group.data-kasm_target_group.arn
target_group_arn = aws_lb_target_group.public.arn
}
tags = {
@ -56,8 +31,8 @@ resource "aws_lb_listener" "kasm-alb-listener" {
}
}
resource "aws_lb_listener" "kasm_alb_listener_http" {
load_balancer_arn = data.aws_lb.data-kasm_alb.arn
resource "aws_lb_listener" "http" {
load_balancer_arn = aws_lb.public.arn
port = "80"
protocol = "HTTP"
@ -76,44 +51,45 @@ resource "aws_lb_listener" "kasm_alb_listener_http" {
}
}
resource "aws_lb_target_group_attachment" "kasm-target-group-attachment" {
count = var.num_webapps
target_group_arn = data.aws_lb_target_group.data-kasm_target_group.arn
target_id = data.aws_instance.data-kasm_web_app[count.index].id
resource "aws_lb_target_group" "public" {
name = "${var.project_name}-target-group"
port = 443
protocol = "HTTPS"
vpc_id = aws_vpc.this.id
health_check {
path = "/api/__healthcheck"
matcher = 200
protocol = "HTTPS"
}
tags = {
Name = "${var.project_name}-kasm-public-tg"
}
}
resource "aws_lb_target_group_attachment" "public" {
count = var.num_webapps
target_group_arn = aws_lb_target_group.public.arn
target_id = aws_instance.webapp[count.index].id
port = 443
}
resource "aws_route53_record" "kasm-route53-elb-record" {
zone_id = data.aws_route53_zone.kasm-route53-zone.zone_id
name = "${var.kasm_zone_name}-lb.${var.aws_domain_name}"
resource "aws_route53_record" "public" {
zone_id = data.aws_route53_zone.this.zone_id
name = var.aws_domain_name
type = "A"
alias {
name = aws_lb.kasm-alb.dns_name
zone_id = aws_lb.kasm-alb.zone_id
name = aws_lb.public.dns_name
zone_id = aws_lb.public.zone_id
evaluate_target_health = true
}
}
resource "aws_route53_record" "kasm-app-url" {
zone_id = data.aws_route53_zone.kasm-route53-zone.zone_id
name = var.aws_domain_name
type = "A"
set_identifier = "${var.project_name}-${var.kasm_zone_name}-set-id"
alias {
name = data.aws_lb.data-kasm_alb.dns_name
zone_id = data.aws_lb.data-kasm_alb.zone_id
evaluate_target_health = true
}
latency_routing_policy {
region = var.aws_region
}
}
resource "aws_route53_health_check" "kasm-elb-hc" {
fqdn = "${var.kasm_zone_name}-lb.${var.aws_domain_name}"
fqdn = var.aws_domain_name
port = 443
type = "HTTPS"
resource_path = "/api/__healthcheck"
@ -121,6 +97,6 @@ resource "aws_route53_health_check" "kasm-elb-hc" {
request_interval = "30"
tags = {
Name = "hc-${var.kasm_zone_name}-lb.${var.aws_domain_name}"
Name = "hc-${var.aws_domain_name}"
}
}

View file

@ -1,9 +1,9 @@
resource "aws_route_table" "internet_access" {
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
resource "aws_route_table" "ig" {
vpc_id = aws_vpc.this.id
route {
cidr_block = var.anywhere
gateway_id = data.aws_internet_gateway.data-kasm-default-ig.id
gateway_id = aws_internet_gateway.this.id
}
tags = {
@ -11,27 +11,12 @@ resource "aws_route_table" "internet_access" {
}
}
data "aws_route_table" "data-internet_gateway_route_table" {
route_table_id = aws_route_table.internet_access.id
}
resource "aws_route_table_association" "webapp_route_association" {
count = var.num_webapps
subnet_id = data.aws_subnet.data-kasm_webapp_subnets[count.index].id
route_table_id = data.aws_route_table.data-internet_gateway_route_table.id
}
resource "aws_route_table_association" "db_route_association" {
subnet_id = data.aws_subnet.data-kasm_db_subnet.id
route_table_id = data.aws_route_table.data-internet_gateway_route_table.id
}
resource "aws_route_table" "nat_route_table" {
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
resource "aws_route_table" "nat" {
vpc_id = aws_vpc.this.id
route {
cidr_block = var.anywhere
gateway_id = data.aws_nat_gateway.data-agent_and_guac_natgw.id
cidr_block = var.anywhere
nat_gateway_id = aws_nat_gateway.this.id
}
tags = {
@ -39,16 +24,40 @@ resource "aws_route_table" "nat_route_table" {
}
}
data "aws_route_table" "data-nat_route_table" {
route_table_id = aws_route_table.nat_route_table.id
resource "aws_route_table_association" "alb" {
count = 2
subnet_id = aws_subnet.alb[count.index].id
route_table_id = aws_route_table.ig.id
}
resource "aws_route_table_association" "agent_nat_route_table_association" {
subnet_id = data.aws_subnet.data-kasm_agent_subnet.id
route_table_id = data.aws_route_table.data-nat_route_table.id
resource "aws_route_table_association" "webapp" {
count = var.num_webapps
subnet_id = aws_subnet.webapp[count.index].id
route_table_id = aws_route_table.nat.id
}
resource "aws_route_table_association" "guac_nat_route_table_association" {
subnet_id = data.aws_subnet.data-kasm_guac_subnet.id
route_table_id = data.aws_route_table.data-nat_route_table.id
resource "aws_route_table_association" "db" {
subnet_id = aws_subnet.db.id
route_table_id = aws_route_table.nat.id
}
resource "aws_route_table_association" "cpx" {
count = var.num_cpx_nodes > 0 ? 1 : 0
subnet_id = one(aws_subnet.cpx[*].id)
route_table_id = aws_route_table.nat.id
}
resource "aws_route_table_association" "agent" {
subnet_id = aws_subnet.agent.id
route_table_id = aws_route_table.ig.id
}
resource "aws_route_table_association" "windows" {
count = var.num_cpx_nodes > 0 ? 1 : 0
subnet_id = one(aws_subnet.windows[*].id)
route_table_id = aws_route_table.ig.id
}

View file

@ -1,174 +1,287 @@
resource "aws_security_group" "kasm-default-elb-sg" {
name = "${var.project_name}-kasm-allow-elb-access"
resource "aws_security_group" "public_lb" {
name = "${var.project_name}-kasm-allow-public-lb-access"
description = "Security Group for ELB"
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = var.web_access_cidrs
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = var.web_access_cidrs
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = [var.anywhere]
}
vpc_id = aws_vpc.this.id
tags = {
Name = "${var.project_name}-kasm-allow-access"
Name = "${var.project_name}-kasm-public-lb-access"
}
}
data "aws_security_group" "data-kasm_default_elb_sg" {
id = aws_security_group.kasm-default-elb-sg.id
resource "aws_security_group_rule" "public_lb_ingress" {
for_each = var.public_lb_security_rules
security_group_id = aws_security_group.public_lb.id
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = var.web_access_cidrs
}
resource "aws_security_group" "kasm-webapp-sg" {
name = "${var.project_name}-${var.kasm_zone_name}-kasm-webapp"
resource "aws_security_group_rule" "public_lb_egress" {
for_each = var.default_egress
security_group_id = aws_security_group.public_lb.id
type = "egress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = each.value.cidr_subnets
}
resource "aws_security_group" "private_lb" {
name = "${var.project_name}-kasm-allow-private-lb-access"
description = "Security Group for ELB"
vpc_id = aws_vpc.this.id
tags = {
Name = "${var.project_name}-kasm-private-lb-access"
}
}
resource "aws_security_group_rule" "private_lb_egress" {
for_each = var.private_lb_security_rules
security_group_id = aws_security_group.private_lb.id
type = "egress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = aws_subnet.webapp[*].cidr_block
}
resource "aws_security_group_rule" "private_lb_agent" {
for_each = var.private_lb_security_rules
security_group_id = aws_security_group.private_lb.id
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
source_security_group_id = aws_security_group.agent.id
}
resource "aws_security_group" "webapp" {
name = "${var.project_name}-kasm-webapp"
description = "Allow access to webapps"
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
vpc_id = aws_vpc.this.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = var.ssh_access_cidrs
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
security_groups = [data.aws_security_group.data-kasm_default_elb_sg.id]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = [
data.aws_subnet.data-kasm_agent_subnet.cidr_block,
data.aws_subnet.data-kasm_guac_subnet.cidr_block
]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = [var.anywhere]
tags = {
Name = "${var.project_name}-kasm-webapp-access"
}
}
data "aws_security_group" "data-kasm_webapp_sg" {
id = aws_security_group.kasm-webapp-sg.id
resource "aws_security_group_rule" "webapp_agent_ingress" {
security_group_id = aws_security_group.webapp.id
type = "ingress"
from_port = var.webapp_security_rules.from_port
to_port = var.webapp_security_rules.to_port
protocol = var.webapp_security_rules.protocol
source_security_group_id = aws_security_group.agent.id
}
resource "aws_security_group" "kasm-agent-sg" {
name = "${var.project_name}-${var.kasm_zone_name}-kasm-agent-access"
resource "aws_security_group_rule" "webapp_private_lb_ingress" {
security_group_id = aws_security_group.webapp.id
type = "ingress"
from_port = var.webapp_security_rules.from_port
to_port = var.webapp_security_rules.to_port
protocol = var.webapp_security_rules.protocol
source_security_group_id = aws_security_group.private_lb.id
}
resource "aws_security_group_rule" "webapp_public_lb_ingress" {
security_group_id = aws_security_group.webapp.id
type = "ingress"
from_port = var.webapp_security_rules.from_port
to_port = var.webapp_security_rules.to_port
protocol = var.webapp_security_rules.protocol
source_security_group_id = aws_security_group.public_lb.id
}
resource "aws_security_group_rule" "webapp_egress" {
for_each = var.default_egress
security_group_id = aws_security_group.webapp.id
type = "egress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = each.value.cidr_subnets
}
resource "aws_security_group" "agent" {
name = "${var.project_name}-kasm-agent-access"
description = "Allow access to agents"
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
vpc_id = aws_vpc.this.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = var.ssh_access_cidrs
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = data.aws_subnet.data-kasm_webapp_subnets[*].cidr_block
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = [var.anywhere]
tags = {
Name = "${var.project_name}-kasm-agent-access"
}
}
data "aws_security_group" "data-kasm_agent_sg" {
id = aws_security_group.kasm-agent-sg.id
resource "aws_security_group_rule" "agent" {
for_each = var.agent_security_rules
security_group_id = aws_security_group.agent.id
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
source_security_group_id = aws_security_group.webapp.id
}
resource "aws_security_group" "kasm-db-sg" {
name = "${var.project_name}-${var.kasm_zone_name}-kasm-db-access"
resource "aws_security_group_rule" "agent_egress" {
for_each = var.default_egress
security_group_id = aws_security_group.agent.id
type = "egress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = each.value.cidr_subnets
}
resource "aws_security_group" "db" {
name = "${var.project_name}-kasm-db-access"
description = "Allow access to webapps"
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
vpc_id = aws_vpc.this.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = var.ssh_access_cidrs
}
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
cidr_blocks = data.aws_subnet.data-kasm_webapp_subnets[*].cidr_block
}
ingress {
from_port = 6379
to_port = 6379
protocol = "tcp"
cidr_blocks = data.aws_subnet.data-kasm_webapp_subnets[*].cidr_block
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = [var.anywhere]
tags = {
Name = "${var.project_name}-kasm-db-access"
}
}
data "aws_security_group" "data-kasm_db_sg" {
id = aws_security_group.kasm-db-sg.id
resource "aws_security_group_rule" "db" {
for_each = var.db_security_rules
security_group_id = aws_security_group.db.id
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
source_security_group_id = aws_security_group.webapp.id
}
resource "aws_security_group" "kasm-guac-sg" {
name = "${var.project_name}-${var.kasm_zone_name}-kasm-guac-access"
description = "Allow access to guac RDP nodes"
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
resource "aws_security_group_rule" "db_egress" {
for_each = var.default_egress
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = var.ssh_access_cidrs
}
security_group_id = aws_security_group.db.id
type = "egress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = each.value.cidr_subnets
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = data.aws_subnet.data-kasm_webapp_subnets[*].cidr_block
}
resource "aws_security_group" "cpx" {
count = var.num_cpx_nodes > 0 ? 1 : 0
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = [var.anywhere]
name = "${var.project_name}-kasm-cpx-access"
description = "Allow access to cpx RDP nodes"
vpc_id = aws_vpc.this.id
tags = {
Name = "${var.project_name}-kasm-cpx-access"
}
}
data "aws_security_group" "data-kasm_guac_sg" {
id = aws_security_group.kasm-guac-sg.id
resource "aws_security_group_rule" "cpx" {
for_each = var.num_cpx_nodes > 0 ? var.cpx_security_rules : {}
security_group_id = one(aws_security_group.cpx[*].id)
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
source_security_group_id = aws_security_group.webapp.id
}
resource "aws_security_group_rule" "private_lb_cpx" {
count = var.num_cpx_nodes > 0 ? 1 : 0
security_group_id = aws_security_group.private_lb.id
type = "ingress"
from_port = var.private_lb_security_rules.https.from_port
to_port = var.private_lb_security_rules.https.to_port
protocol = var.private_lb_security_rules.https.protocol
source_security_group_id = one(aws_security_group.cpx[*].id)
}
resource "aws_security_group_rule" "webapp_cpx" {
count = var.num_cpx_nodes > 0 ? 1 : 0
security_group_id = aws_security_group.webapp.id
type = "ingress"
from_port = var.webapp_security_rules.from_port
to_port = var.webapp_security_rules.to_port
protocol = var.webapp_security_rules.protocol
source_security_group_id = one(aws_security_group.cpx[*].id)
}
resource "aws_security_group_rule" "cpx_egress" {
for_each = var.num_cpx_nodes > 0 ? var.default_egress : {}
security_group_id = one(aws_security_group.cpx[*].id)
type = "egress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = each.value.cidr_subnets
}
resource "aws_security_group" "windows" {
count = var.num_cpx_nodes > 0 ? 1 : 0
name = "${var.project_name}-kasm-windows-access"
description = "Allow access to Windows servers"
vpc_id = aws_vpc.this.id
tags = {
Name = "${var.project_name}-kasm-windows-access"
}
}
resource "aws_security_group_rule" "windows" {
for_each = var.num_cpx_nodes > 0 ? var.windows_security_rules : {}
security_group_id = one(aws_security_group.windows[*].id)
type = "ingress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
source_security_group_id = can(regex("(?i:cpx)", each.key)) ? one(aws_security_group.cpx[*].id) : aws_security_group.webapp.id
}
resource "aws_security_group_rule" "private_lb_windows" {
count = var.num_cpx_nodes > 0 ? 1 : 0
security_group_id = aws_security_group.private_lb.id
type = "ingress"
from_port = var.private_lb_security_rules.https.from_port
to_port = var.private_lb_security_rules.https.to_port
protocol = var.private_lb_security_rules.https.protocol
source_security_group_id = one(aws_security_group.windows[*].id)
}
resource "aws_security_group_rule" "webapp_windows" {
count = var.num_cpx_nodes > 0 ? 1 : 0
security_group_id = aws_security_group.webapp.id
type = "ingress"
from_port = var.webapp_security_rules.from_port
to_port = var.webapp_security_rules.to_port
protocol = var.webapp_security_rules.protocol
source_security_group_id = one(aws_security_group.windows[*].id)
}
resource "aws_security_group_rule" "windows_egress" {
for_each = var.num_cpx_nodes > 0 ? var.default_egress : {}
security_group_id = one(aws_security_group.windows[*].id)
type = "egress"
from_port = each.value.from_port
to_port = each.value.to_port
protocol = each.value.protocol
cidr_blocks = each.value.cidr_subnets
}

View file

@ -0,0 +1,35 @@
data "aws_iam_policy_document" "this" {
statement {
effect = "Allow"
principals {
type = "Service"
identifiers = [
"ec2.amazonaws.com"
]
}
actions = ["sts:AssumeRole"]
}
}
resource "aws_iam_role" "this" {
count = var.create_aws_ssm_iam_role ? 1 : 0
name = var.aws_ssm_iam_role_name != "" ? var.aws_ssm_iam_role_name : "Kasm_SSM_IAM_Instance_Role"
assume_role_policy = data.aws_iam_policy_document.this.json
}
resource "aws_iam_role_policy_attachment" "this" {
count = var.create_aws_ssm_iam_role ? 1 : 0
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
role = one(aws_iam_role.this[*].name)
}
resource "aws_iam_instance_profile" "this" {
count = var.create_aws_ssm_iam_role ? 1 : 0
name = var.aws_ssm_instance_profile_name != "" ? var.aws_ssm_instance_profile_name : "Kasm_SSM_Instance_Profile"
role = one(aws_iam_role.this[*].name)
}

View file

@ -2,68 +2,81 @@ locals {
kasm_vpc_subnet_cidr_mask = split("/", var.vpc_subnet_cidr)[1]
kasm_server_subnet_cidr_calculation = (8 - (local.kasm_vpc_subnet_cidr_mask - 16))
kasm_server_subnet_cidr_size = local.kasm_server_subnet_cidr_calculation < 3 ? 3 : local.kasm_server_subnet_cidr_calculation
kasm_agent_subnet_id = (var.num_webapps + 1)
}
## Will create Agent subnet x.x.0.x/24 (assuming a VPC Subnet CIDR between x.x.0.0/16 and x.x.0.0/21)
resource "aws_subnet" "kasm-db-subnet" {
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
cidr_block = cidrsubnet(var.vpc_subnet_cidr, local.kasm_server_subnet_cidr_size, 0)
## Will create Agent subnet x.x.0.0/24 and x.x.1.0/24 (assuming a VPC Subnet CIDR between x.x.0.0/16 and x.x.0.0/21)
resource "aws_subnet" "alb" {
count = 2
vpc_id = aws_vpc.this.id
cidr_block = cidrsubnet(var.vpc_subnet_cidr, local.kasm_server_subnet_cidr_size, count.index)
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-kasm-lb-subnet-${count.index}"
}
}
## Will create WebApp subnets x.x.2.0/24 and x.x.3.0/24 (assuming a VPC Subnet CIDR between x.x.0.0/16 and x.x.0.0/21)
resource "aws_subnet" "webapp" {
count = var.num_webapps
vpc_id = aws_vpc.this.id
cidr_block = cidrsubnet(var.vpc_subnet_cidr, local.kasm_server_subnet_cidr_size, (count.index + 2))
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {
Name = "${var.project_name}-kasm-webapp-subnet-${count.index}"
}
}
## Will create Agent subnet x.x.4.0/24 (assuming a VPC Subnet CIDR between x.x.0.0/16 and x.x.0.0/21)
resource "aws_subnet" "db" {
vpc_id = aws_vpc.this.id
cidr_block = cidrsubnet(var.vpc_subnet_cidr, local.kasm_server_subnet_cidr_size, 4)
availability_zone = data.aws_availability_zones.available.names[1]
tags = {
Name = "${var.project_name}-kasm-db-subnet"
}
}
data "aws_subnet" "data-kasm_db_subnet" {
id = aws_subnet.kasm-db-subnet.id
}
## Will create WebApp subnets x.x.1.x/24 and x.x.2.x/24 (assuming a VPC Subnet CIDR between x.x.0.0/16 and x.x.0.0/21)
resource "aws_subnet" "kasm-webapp-subnets" {
count = var.num_webapps
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
cidr_block = cidrsubnet(var.vpc_subnet_cidr, local.kasm_server_subnet_cidr_size, (count.index + 1))
availability_zone = data.aws_availability_zones.available.names[count.index]
## Will create Agent subnet x.x.6.0/24 (assuming a VPC Subnet CIDR between x.x.0.0/16 and x.x.0.0/21)
resource "aws_subnet" "agent" {
vpc_id = aws_vpc.this.id
cidr_block = cidrsubnet(var.vpc_subnet_cidr, local.kasm_server_subnet_cidr_size, 5)
map_public_ip_on_launch = true
availability_zone = data.aws_availability_zones.available.names[1]
tags = {
Name = "${var.project_name}-kasm-webapp-subnet"
Name = "${var.project_name}-agent-subnet"
}
}
data "aws_subnet" "data-kasm_webapp_subnets" {
count = var.num_webapps
id = aws_subnet.kasm-webapp-subnets[count.index].id
}
## Will create CPX subnet x.x.5.0/24 (assuming a VPC Subnet CIDR between x.x.0.0/16 and x.x.0.0/21)
resource "aws_subnet" "cpx" {
count = var.num_cpx_nodes > 0 ? 1 : 0
## Will create Agent subnet x.x.3.x/24 (assuming a VPC Subnet CIDR between x.x.0.0/16 and x.x.0.0/21)
resource "aws_subnet" "kasm-agent-subnet" {
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
cidr_block = cidrsubnet(var.vpc_subnet_cidr, local.kasm_server_subnet_cidr_size, local.kasm_agent_subnet_id)
map_public_ip_on_launch = true
vpc_id = aws_vpc.this.id
cidr_block = cidrsubnet(var.vpc_subnet_cidr, local.kasm_server_subnet_cidr_size, 6)
availability_zone = data.aws_availability_zones.available.names[0]
tags = {
Name = "${var.project_name}-agent-natgw-subnet"
Name = "${var.project_name}-cpx-subnet"
}
}
data "aws_subnet" "data-kasm_agent_subnet" {
id = aws_subnet.kasm-agent-subnet.id
}
## Will create cpx subnet x.x.7.0/24 (assuming a VPC Subnet CIDR between x.x.0.0/16 and x.x.0.0/21)
resource "aws_subnet" "windows" {
count = var.num_cpx_nodes > 0 ? 1 : 0
## Will create Guac subnet x.x.4.x/24 (assuming a VPC Subnet CIDR between x.x.0.0/16 and x.x.0.0/21)
resource "aws_subnet" "kasm-guac-subnet" {
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
cidr_block = cidrsubnet(var.vpc_subnet_cidr, local.kasm_server_subnet_cidr_size, (local.kasm_agent_subnet_id + 1))
vpc_id = aws_vpc.this.id
cidr_block = cidrsubnet(var.vpc_subnet_cidr, local.kasm_server_subnet_cidr_size, 7)
map_public_ip_on_launch = true
availability_zone = data.aws_availability_zones.available.names[1]
tags = {
Name = "${var.project_name}-guac-natgw-subnet"
Name = "${var.project_name}-windows-subnet"
}
}
data "aws_subnet" "data-kasm_guac_subnet" {
id = aws_subnet.kasm-guac-subnet.id
}

View file

@ -24,6 +24,6 @@ do
done
echo "WebApp is alive"
bash kasm_release/install.sh -S agent -e -p $PRIVATE_IP -m ${manager_address} -M ${manager_token}
bash kasm_release/install.sh -S agent -e -H -p $PRIVATE_IP -m ${manager_address} -M ${manager_token}
echo "Done"

View file

@ -24,6 +24,6 @@ do
done
echo "WebApp is alive"
bash kasm_release/install.sh -S guac -e -p $PRIVATE_IP -n ${manager_address} -k ${service_registration_token}
bash kasm_release/install.sh -S guac -e -H -p $PRIVATE_IP -n ${manager_address} -k ${service_registration_token}
echo "Done"

View file

@ -11,10 +11,8 @@ echo '/var/swap.1 swap swap defaults 0 0' | tee -a /etc/fstab
cd /tmp
PRIVATE_IP=(`hostname -I | cut -d ' ' -f1 | tr -d '\\n'`)
wget ${kasm_build_url} -O kasm_workspaces.tar.gz
tar -xf kasm_workspaces.tar.gz
bash kasm_release/install.sh -S db -e -Q ${database_password} -R ${redis_password} -U ${user_password} -P ${admin_password} -M ${manager_token} -k ${service_registration_token}
bash kasm_release/install.sh -S db -e -H -Q ${database_password} -R ${redis_password} -U ${user_password} -P ${admin_password} -M ${manager_token} -k ${service_registration_token}
echo "Done"

View file

@ -11,8 +11,6 @@ echo '/var/swap.1 swap swap defaults 0 0' | tee -a /etc/fstab
cd /tmp
PRIVATE_IP=(`hostname -I | cut -d ' ' -f1 | tr -d '\\n'`)
wget ${kasm_build_url} -O kasm_workspaces.tar.gz
tar -xf kasm_workspaces.tar.gz
@ -31,6 +29,6 @@ done
echo "Redis is alive"
bash kasm_release/install.sh -S app -e -z ${zone_name} -q "${db_ip}" -Q ${database_password} -R ${redis_password}
bash kasm_release/install.sh -S app -e -H -z ${zone_name} -q "${db_ip}" -Q ${database_password} -R ${redis_password}
echo "Done"

View file

@ -31,8 +31,8 @@ variable "num_agents" {
default = 2
}
variable "num_guac_nodes" {
description = "The number of Agent Role Servers to create in the deployment"
variable "num_cpx_nodes" {
description = "The number of cpx RDP Role Servers to create in the deployment"
type = number
default = 2
}
@ -55,16 +55,30 @@ variable "agent_instance_type" {
default = "t3.medium"
}
variable "guac_instance_type" {
description = "The instance type for the Guacamole RDP nodes"
variable "cpx_instance_type" {
description = "The instance type for the cpxamole RDP nodes"
type = string
default = "t3.medium"
}
variable "ssh_access_cidrs" {
description = "CIDR notation of the bastion host allowed to SSH in to the machines"
type = list(string)
default = ["0.0.0.0/0"]
variable "webapp_hdd_size_gb" {
description = "The HDD size for Kasm Webapp nodes"
type = number
}
variable "db_hdd_size_gb" {
description = "The HDD size for Kasm DB"
type = number
}
variable "cpx_hdd_size_gb" {
description = "The HDD size for Kasm Guac RDP nodes"
type = number
}
variable "agent_hdd_size_gb" {
description = "The HDD size for Kasm Agent nodes"
type = number
}
variable "web_access_cidrs" {
@ -93,6 +107,24 @@ variable "swap_size" {
type = number
}
variable "create_aws_ssm_iam_role" {
description = "Create an AWS SSM IAM role to attach to VMs for SSH/console access to VMs."
type = bool
default = false
}
variable "aws_ssm_iam_role_name" {
description = "The name of the SSM EC2 role to associate with Kasm VMs for SSH access"
type = string
default = ""
}
variable "aws_ssm_instance_profile_name" {
description = "The name of the SSM EC2 Instance Profile to associate with Kasm VMs for SSH access"
type = string
default = ""
}
variable "database_password" {
description = "The password for the database. No special characters"
type = string
@ -124,7 +156,7 @@ variable "manager_token" {
}
variable "service_registration_token" {
description = "The service registration token value for Guac RDP servers to authenticate to webapps. No special characters"
description = "The service registration token value for cpx RDP servers to authenticate to webapps. No special characters"
type = string
sensitive = true
}
@ -145,3 +177,159 @@ variable "anywhere" {
error_message = "Anywhere variable must be valid IPv4 CIDR - usually 0.0.0.0/0 for all default routes and default Security Group access."
}
}
variable "public_lb_security_rules" {
description = "A map of objects of security rules to apply to the Public ALB"
type = map(object({
from_port = number
to_port = number
protocol = string
}))
default = {
https = {
from_port = 443
to_port = 443
protocol = "tcp"
}
http = {
from_port = 80
to_port = 80
protocol = "tcp"
}
}
}
variable "private_lb_security_rules" {
description = "A map of objects of security rules to apply to the Private ALB"
type = map(object({
from_port = number
to_port = number
protocol = string
}))
default = {
https = {
from_port = 443
to_port = 443
protocol = "tcp"
}
}
}
variable "webapp_security_rules" {
description = "A map of objects of security rules to apply to the Kasm WebApp server"
type = object({
from_port = number
to_port = number
protocol = string
})
default = {
from_port = 443
to_port = 443
protocol = "tcp"
}
}
variable "db_security_rules" {
description = "A map of objects of security rules to apply to the Kasm DB"
type = map(object({
from_port = number
to_port = number
protocol = string
}))
default = {
postgres = {
from_port = 5432
to_port = 5432
protocol = "tcp"
}
redis = {
from_port = 6379
to_port = 6379
protocol = "tcp"
}
}
}
variable "cpx_security_rules" {
description = "A map of objects of security rules to apply to the Kasm Connection Proxy server"
type = map(object({
from_port = number
to_port = number
protocol = string
}))
default = {
https = {
from_port = 443
to_port = 443
protocol = "tcp"
}
}
}
variable "agent_security_rules" {
description = "A map of objects of security rules to apply to the Kasm WebApp server"
type = map(object({
from_port = number
to_port = number
protocol = string
}))
default = {
https = {
from_port = 443
to_port = 443
protocol = "tcp"
}
}
}
variable "windows_security_rules" {
description = "A map of objects of security rules to apply to the Kasm Windows VMs"
type = map(object({
from_port = number
to_port = number
protocol = string
}))
default = {
cpx_rdp = {
from_port = 3389
to_port = 3389
protocol = "tcp"
}
cpx_api = {
from_port = 4902
to_port = 4902
protocol = "tcp"
}
webapp_api = {
from_port = 4902
to_port = 4902
protocol = "tcp"
}
}
}
variable "default_egress" {
description = "Default egress security rule for all security groups"
type = map(object({
from_port = number
to_port = number
protocol = string
cidr_subnets = list(string)
}))
default = {
all = {
from_port = 0
to_port = 0
protocol = "-1"
cidr_subnets = ["0.0.0.0/0"]
}
}
}

View file

@ -1,23 +1,36 @@
resource "aws_vpc" "kasm-default-vpc" {
resource "aws_vpc" "this" {
cidr_block = var.vpc_subnet_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project_name}-kasm-db-vpc"
Name = "${var.project_name}-kasm-vpc"
}
}
data "aws_vpc" "data-kasm-default-vpc" {
id = aws_vpc.kasm-default-vpc.id
}
resource "aws_internet_gateway" "this" {
vpc_id = aws_vpc.this.id
resource "aws_internet_gateway" "kasm-default-ig" {
vpc_id = data.aws_vpc.data-kasm-default-vpc.id
tags = {
Name = "${var.project_name}-kasm-ig"
}
}
data "aws_internet_gateway" "data-kasm-default-ig" {
internet_gateway_id = aws_internet_gateway.kasm-default-ig.id
resource "aws_eip" "this" {
domain = "vpc"
tags = {
Name = "${var.project_name}-kasm-nat-gateway-eip"
}
}
resource "aws_nat_gateway" "this" {
allocation_id = aws_eip.this.id
subnet_id = aws_subnet.alb[0].id
tags = {
Name = "${var.project_name}-kasm-nat-gateway"
}
depends_on = [aws_internet_gateway.this]
}

View file

@ -1,20 +1,21 @@
resource "aws_instance" "kasm-web-app" {
count = var.num_webapps
ami = var.ec2_ami
instance_type = var.webapp_instance_type
vpc_security_group_ids = [data.aws_security_group.data-kasm_webapp_sg.id]
subnet_id = data.aws_subnet.data-kasm_webapp_subnets[count.index].id
key_name = var.aws_key_pair
associate_public_ip_address = true
resource "aws_instance" "webapp" {
count = var.num_webapps
ami = var.ec2_ami
instance_type = var.webapp_instance_type
vpc_security_group_ids = [aws_security_group.webapp.id]
subnet_id = aws_subnet.webapp[count.index].id
key_name = var.aws_key_pair
iam_instance_profile = one(aws_iam_instance_profile.this[*].id)
root_block_device {
volume_size = 40
volume_size = var.webapp_hdd_size_gb
}
user_data = templatefile("${path.module}/userdata/webapp_bootstrap.sh",
{
kasm_build_url = var.kasm_build
db_ip = data.aws_instance.data-kasm_db.private_ip
db_ip = aws_instance.db.private_ip
database_password = var.database_password
redis_password = var.redis_password
swap_size = var.swap_size
@ -22,12 +23,14 @@ resource "aws_instance" "kasm-web-app" {
}
)
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
instance_metadata_tags = null
}
tags = {
Name = "${var.project_name}-${var.kasm_zone_name}-kasm-webapp"
Name = "${var.project_name}-${var.kasm_zone_name}-kasm-webapp-${count.index}"
}
}
data "aws_instance" "data-kasm_web_app" {
count = var.num_webapps
instance_id = aws_instance.kasm-web-app[count.index].id
}

7
aws/standard/output.tf Normal file
View file

@ -0,0 +1,7 @@
output "kasm_zone_settings" {
description = "Upstream Auth settings to apply to Kasm Zone configuration"
value = <<ZONE
Kam Zone configuration for zone: default
Upstream Auth address: ${var.aws_region}-private.${var.aws_domain_name}
ZONE
}

View file

@ -1,8 +1,10 @@
terraform {
required_version = "~> 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
#version = "4.56.0"
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}

View file

@ -0,0 +1,2 @@
aws_access_key = ""
aws_secret_key = ""

View file

@ -1,43 +0,0 @@
## AWS Environment settings
aws_key_pair = "troy-common-ssh"
aws_region = "us-east-1"
aws_domain_name = "bryan-test2.sandbox1.kasmweb.net"
vpc_subnet_cidr = "10.0.0.0/16"
## Kasm deployment settings
kasm_zone_name = "default"
project_name = "contoso"
num_agents = 2
num_webapps = 2
num_guac_nodes = 1
## Kasm Server settings
agent_instance_type = "t3.medium"
guac_instance_type = "t3.medium"
webapp_instance_type = "t3.small"
db_instance_type = "t3.small"
ec2_ami = "ami-09cd747c78a9add63"
swap_size = 2048
## VM Access subnets
ssh_access_cidrs = ["0.0.0.0/0"]
web_access_cidrs = ["0.0.0.0/0"]
## Kasm passwords
database_password = "changeme"
redis_password = "changeme"
user_password = "changeme"
admin_password = "changeme"
manager_token = "changeme"
service_registration_token = "changeme"
## Kasm download URL
kasm_build = "https://kasm-static-content.s3.amazonaws.com/kasm_release_1.13.0.002947.tar.gz"
## Default tags for all AWS resources
aws_default_tags = {
Deployed_by = "Terraform"
Deployment_type = "Multi-Server"
Service_name = "Kasm Workspaces"
Kasm_version = "1.12"
}

View file

@ -0,0 +1,61 @@
## AWS Environment settings
aws_key_pair = ""
aws_region = ""
aws_domain_name = ""
vpc_subnet_cidr = "10.0.0.0/16"
## Kasm deployment settings
kasm_zone_name = "default"
project_name = "test"
## Number of each Kasm role to deploy
num_agents = 2
num_webapps = 2
num_cpx_nodes = 1
## VM Public Access subnets
web_access_cidrs = ["0.0.0.0/0"]
## AWS SSM setup for console/SSH access to VMs behind NAT gateway
create_aws_ssm_iam_role = true
aws_ssm_iam_role_name = ""
aws_ssm_instance_profile_name = ""
## Kasm Server settings
ec2_ami_id = ""
swap_size = 2048
## Kasm Webapp Instance Settings
webapp_instance_type = "t3.small"
webapp_hdd_size_gb = 50
## Kasm DB Instance Settings
db_instance_type = "t3.medium"
db_hdd_size_gb = 80
## Kasm Agent Instance Settings
agent_instance_type = "t3.medium"
agent_hdd_size_gb = 150
## Kasm CPX Instance Settings
cpx_instance_type = "t3.small"
cpx_hdd_size_gb = 50
## Kasm passwords
database_password = "changeme"
redis_password = "changeme"
user_password = "changeme"
admin_password = "changeme"
manager_token = "changeme"
service_registration_token = "changeme"
## Kasm download URL
kasm_build = "https://kasm-static-content.s3.amazonaws.com/kasm_release_1.14.0.3a7abb.tar.gz"
## Default tags for all AWS resources
aws_default_tags = {
Deployed_by = "Terraform"
Deployment_type = "Multi-Server"
Service_name = "Kasm Workspaces"
Kasm_version = "1.14"
}

View file

@ -96,7 +96,6 @@ variable "swap_size" {
variable "webapp_instance_type" {
description = "The instance type for the webapps"
type = string
default = "t3.small"
validation {
condition = can(regex("^(([a-z-]{1,3})(\\d{1,2})?(\\w{1,4})?)\\.(nano|micro|small|medium|metal|large|(2|3|4|6|8|9|10|12|16|18|24|32|48|56|112)?xlarge)", var.webapp_instance_type))
@ -107,7 +106,6 @@ variable "webapp_instance_type" {
variable "db_instance_type" {
description = "The instance type for the Database"
type = string
default = "t3.small"
validation {
condition = can(regex("^(([a-z-]{1,3})(\\d{1,2})?(\\w{1,4})?)\\.(nano|micro|small|medium|metal|large|(2|3|4|6|8|9|10|12|16|18|24|32|48|56|112)?xlarge)", var.db_instance_type))
@ -118,7 +116,6 @@ variable "db_instance_type" {
variable "agent_instance_type" {
description = "The instance type for the Agents"
type = string
default = "t3.medium"
validation {
condition = can(regex("^(([a-z-]{1,3})(\\d{1,2})?(\\w{1,4})?)\\.(nano|micro|small|medium|metal|large|(2|3|4|6|8|9|10|12|16|18|24|32|48|56|112)?xlarge)", var.agent_instance_type))
@ -126,32 +123,29 @@ variable "agent_instance_type" {
}
}
variable "guac_instance_type" {
description = "The instance type for the Guacamole RDP nodes"
variable "cpx_instance_type" {
description = "The instance type for the cpxamole RDP nodes"
type = string
default = "t3.medium"
validation {
condition = can(regex("^(([a-z-]{1,3})(\\d{1,2})?(\\w{1,4})?)\\.(nano|micro|small|medium|metal|large|(2|3|4|6|8|9|10|12|16|18|24|32|48|56|112)?xlarge)", var.guac_instance_type))
error_message = "Check the guac_instance_type variable and ensure it is a valid AWS Instance type (https://aws.amazon.com/ec2/instance-types/)."
condition = can(regex("^(([a-z-]{1,3})(\\d{1,2})?(\\w{1,4})?)\\.(nano|micro|small|medium|metal|large|(2|3|4|6|8|9|10|12|16|18|24|32|48|56|112)?xlarge)", var.cpx_instance_type))
error_message = "Check the cpx_instance_type variable and ensure it is a valid AWS Instance type (https://aws.amazon.com/ec2/instance-types/)."
}
}
variable "num_webapps" {
description = "The number of WebApp role servers to create in the deployment"
type = number
default = 2
validation {
condition = var.num_webapps >= 1 && var.num_webapps <= 3 && floor(var.num_webapps) == var.num_webapps
error_message = "Acceptable number of webapps range between 1-3."
condition = var.num_webapps >= 1 && var.num_webapps <= 6 && floor(var.num_webapps) == var.num_webapps
error_message = "Acceptable number of webapps range between 1-6."
}
}
variable "num_agents" {
description = "The number of Agent Role Servers to create in the deployment"
type = number
default = 2
validation {
condition = var.num_agents >= 0 && var.num_agents <= 100 && floor(var.num_agents) == var.num_agents
@ -159,32 +153,59 @@ variable "num_agents" {
}
}
variable "num_guac_nodes" {
variable "num_cpx_nodes" {
description = "The number of Agent Role Servers to create in the deployment"
type = number
default = 1
validation {
condition = var.num_guac_nodes >= 0 && var.num_guac_nodes <= 100 && floor(var.num_guac_nodes) == var.num_guac_nodes
error_message = "Acceptable number of Kasm Agents range between 0-100."
condition = var.num_cpx_nodes == 0 ? true : var.num_cpx_nodes >= 0 && var.num_cpx_nodes <= 100 && floor(var.num_cpx_nodes) == var.num_cpx_nodes
error_message = "If num_cpx_nodes is set to 0, this Terraform will not deploy the Connection Proxy node. Acceptable number of Kasm Agents range between 0-100."
}
}
variable "ssh_access_cidrs" {
description = "CIDR notation of the bastion host allowed to SSH in to the machines"
type = list(string)
default = ["0.0.0.0/0"]
variable "webapp_hdd_size_gb" {
description = "The HDD size in GB to configure for the Kasm WebApp instances"
type = number
validation {
condition = alltrue([for subnet in var.ssh_access_cidrs : can(cidrhost(subnet, 0))])
error_message = "One of the subnets provided in the ssh_access_cidr variable is invalid."
condition = can(var.webapp_hdd_size_gb >= 40)
error_message = "Kasm Webapps should have at least a 40 GB HDD to ensure enough space for Kasm services."
}
}
variable "db_hdd_size_gb" {
description = "The HDD size in GB to configure for the Kasm Database instances"
type = number
validation {
condition = can(var.db_hdd_size_gb >= 40)
error_message = "Kasm Database should have at least a 40 GB HDD to ensure enough space for Kasm services."
}
}
variable "agent_hdd_size_gb" {
description = "The HDD size in GB to configure for the Kasm Agent instances"
type = number
validation {
condition = can(var.agent_hdd_size_gb >= 120)
error_message = "Kasm Agents should have at least a 120 GB HDD to ensure enough space for Kasm services."
}
}
variable "cpx_hdd_size_gb" {
description = "The HDD size in GB to configure for the Kasm cpx RDP instances"
type = number
validation {
condition = can(var.cpx_hdd_size_gb >= 40)
error_message = "Kasm cpx RDP nodes should have at least a 40 GB HDD to ensure enough space for Kasm services. If num_cpx_nodes is set to 0 this setting is ignored."
}
}
variable "web_access_cidrs" {
description = "CIDR notation of the bastion host allowed to SSH in to the machines"
type = list(string)
default = ["0.0.0.0/0"]
validation {
condition = alltrue([for subnet in var.web_access_cidrs : can(cidrhost(subnet, 0))])
@ -192,12 +213,12 @@ variable "web_access_cidrs" {
}
}
variable "ec2_ami" {
description = "The AMI used for the EC2 nodes. Recommended Ubuntu 20.04 LTS."
variable "ec2_ami_id" {
description = "The AMI used for the EC2 nodes. Recommended Ubuntu 22.04 LTS."
type = string
validation {
condition = can(regex("^(ami-[a-f0-9]{17})", var.ec2_ami))
condition = can(regex("^(ami-[a-f0-9]{17})", var.ec2_ami_id))
error_message = "Please verify that your AMI is in the correct format for AWS (https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/finding-an-ami.html)."
}
}
@ -258,7 +279,7 @@ variable "manager_token" {
}
variable "service_registration_token" {
description = "The service registration token value for Guac RDP servers to authenticate to webapps. No special characters"
description = "The service registration token value for cpx RDP servers to authenticate to webapps. No special characters"
type = string
sensitive = true
@ -268,6 +289,39 @@ variable "service_registration_token" {
}
}
variable "create_aws_ssm_iam_role" {
description = "Create an AWS SSM IAM role to attach to VMs for SSH/console access to VMs."
type = bool
default = false
validation {
condition = can(tobool(var.create_aws_ssm_iam_role))
error_message = "The create_aws_ssm_iam_role is a boolean value and can only be either true or false."
}
}
variable "aws_ssm_iam_role_name" {
description = "The name of the SSM EC2 role to associate with Kasm VMs for SSH access"
type = string
default = ""
validation {
condition = can(regex("[a-zA-Z0-9+=,.@-]{1,64}", var.aws_ssm_iam_role_name))
error_message = "The aws_ssm_iam_role_name must be unique across the account and can only consisit of between 1 and 64 characters consisting of letters, numbers, underscores (_), plus (+), equals (=), comman (,), period (.), at symbol (@), or dash (-)."
}
}
variable "aws_ssm_instance_profile_name" {
description = "The name of the SSM EC2 Instance Profile to associate with Kasm VMs for SSH access"
type = string
default = ""
validation {
condition = var.aws_ssm_instance_profile_name == "" ? true : can(regex("[a-zA-Z0-9+=,.@-]{1,64}", var.aws_ssm_instance_profile_name))
error_message = "The aws_ssm_instance_profile_name must be unique across the account and can only consisit of between 1 and 64 characters consisting of letters, numbers, underscores (_), plus (+), equals (=), comman (,), period (.), at symbol (@), or dash (-)."
}
}
## Non-validated variables
variable "kasm_build" {
description = "The URL for the Kasm Workspaces build"
@ -277,8 +331,5 @@ variable "kasm_build" {
variable "aws_default_tags" {
description = "Default tags to apply to all AWS resources for this deployment"
type = map(any)
default = {
Service_name = "Kasm Workspaces"
Kasm_version = "1.12"
}
default = {}
}

View file

@ -9,13 +9,13 @@ This project will deploy Kasm Workspaces in a single-server deployment on Digita
# Pre-Configuration
### Domain Configuration
If digitalocean is not already managing your domain you will need to have your registrar point to the DigitalOcean nameservers: https://www.digitalocean.com/community/tutorials/how-to-point-to-digitalocean-nameservers-from-common-domain-registrars
If digitalocean is not already managing your domain you will need to have your registrar point to the DigitalOcean nameservers: https://www.digitalocean.com/community/tutorials/how-to-point-to-digitalocean-nameservers-from-common-domain-registrars
### API Tokens
Create a personal access token with read/write permissions at https://cloud.digitalocean.com/account/api/tokens
Create a personal access token with read/write permissions at https://cloud.digitalocean.com/account/api/tokens
### SSH Authorized Keys
This project will launch a droplet and allow connections using the ssh keys defined by `ssh_key_fingerprints`. You can copy the fingerprint from the desired ssh keys from https://cloud.digitalocean.com/account/security
This project will launch a droplet and allow connections using the ssh keys defined by `ssh_key_fingerprints`. You can copy the fingerprint from the desired ssh keys from https://cloud.digitalocean.com/account/security
# Terraform Configuration
@ -29,34 +29,57 @@ This project will launch a droplet and allow connections using the ssh keys defi
3. Verify the configuration
terraform plan -var-file settings.tfvars -var-file secrets.tfvars
terraform plan -var-file secrets.tfvars
4. Deploy
terraform apply -var-file settings.tfvars -var-file secrets.tfvars
terraform apply -var-file secrets.tfvars
5. Login to the Deployment as an Admin via the domain defined e.g `https://kasm.contoso.com`. Single server installs download all workspaces images during the install process so it may take ~15 minutes for the server to fully come online.
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.0 |
| <a name="requirement_digitalocean"></a> [digitalocean](#requirement\_digitalocean) | ~> 2.0 |
# OCI Terraform Variable definitions
## Providers
| Variable | Description | Variable type | Example |
|:--------:|-------------|---------------|---------|
| `digital_ocean_token` | The DigitalOcean authentication token. | String | `"dop_v1_EXAMPLEb8f85b081895f489921abbf26e64d7f3a0e581f8a1d8d532a5ba553"` |
| `digital_ocean_region` | The DigitalOcean region where you wish to deploy Kasm | String | `"nyc3"` |
| `do_domain_name` | The domain name that users will use to access kasm. | String | `"kasm.contoso.com"` |
| `ssh_key_fingerprints` | A list of DigitalOcean SSH fingerprints to use for SSH access to your Kasm server. | List(String) | `["66:e5:d1:85:cd:ba:ca:6a:d0:76:86:ef:1c:11:63:97"]` |
| `project_name` | The name of the deployment (e.g dev, staging). A short single word of up to 15 characters. | String | `"kasm"` |
| `oci_domain_name` | The public Zone used for the dns entries. This must already exist in the OCI account. (e.g kasm.contoso.com). The deployment will be accessed via this zone name using https. | String | `"kasm.contoso.com"` |
| `vpc_subnet_cidr` | The VPC Subnet CIDR where you wish to deploy Kasm | String | `"10.0.0.0/24"` |
| `digital_ocean_droplet_slug` | The Default Digital Ocean Droplet Slug: https://slugs.do-api.dev/ | String | `"s-2vcpu-4gb-intel"` |
| `digital_ocean_image` | Default Image for Ubuntu 20.04 LTS with Docker | String | `"docker-20-04"` |
| `kasm_build_url` | The download URL for the desired Kasm Workspaces version. | String | `"https://kasm-static-content.s3.amazonaws.com/kasm_release_1.13.0.002947.tar.gz"` |
| `admin_password` | The Kasm Administrative user login password. String from 12-30 characters in length with no special characters. | String | `"1qaz2wsx3EDC4RFV"` |
| `user_password` | A Kasm standard (non-administrator) user password. String from 12-30 characters in length with no special characters. | String | `"1qaz2wsx3EDC4RFV"` |
| `allow_ssh_cidrs` | A list of subnets in CIDR notation allowed to SSH into your kasm servers | List(String) | `["10.0.0.0/16","172.217.22.14/32"]` |
| `allow_web_cidrs` | A list of subnets in CIDR notation allowed Web access to your kasm servers | List(String) | `["0.0.0.0/0"]` |
| `swap_size` | The amount of swap (in MB) to configure inside the Kasm servers. | Number | `2048` |
| `instance_shape` | The OCI instance shape to use for Kasm deployment. Kasm recommends using a Flex instance type. | String | `"VM.Standard.E4.Flex"` |
No providers.
## Modules
| Name | Source | Version |
|------|--------|---------|
| <a name="module_kasm"></a> [kasm](#module\_kasm) | ./module | n/a |
## Resources
No resources.
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_admin_password"></a> [admin\_password](#input\_admin\_password) | The default password to be used for the default admin@kasm.local account. Only use alphanumeric characters | `string` | `"changeme"` | no |
| <a name="input_allow_kasm_web_cidrs"></a> [allow\_kasm\_web\_cidrs](#input\_allow\_kasm\_web\_cidrs) | CIDR notation of the bastion host allowed to SSH in to the machines | `list(string)` | <pre>[<br> "0.0.0.0/0"<br>]</pre> | no |
| <a name="input_allow_ssh_cidrs"></a> [allow\_ssh\_cidrs](#input\_allow\_ssh\_cidrs) | CIDR notation of the bastion host allowed to SSH in to the machines | `list(string)` | <pre>[<br> "0.0.0.0/0"<br>]</pre> | no |
| <a name="input_digital_ocean_droplet_slug"></a> [digital\_ocean\_droplet\_slug](#input\_digital\_ocean\_droplet\_slug) | The Default Digital Ocean Droplet Slug: https://slugs.do-api.dev/ | `string` | `"s-2vcpu-4gb-intel"` | no |
| <a name="input_digital_ocean_image"></a> [digital\_ocean\_image](#input\_digital\_ocean\_image) | Default Image for Ubuntu 20.04 LTS with Docker | `string` | `"docker-20-04"` | no |
| <a name="input_digital_ocean_region"></a> [digital\_ocean\_region](#input\_digital\_ocean\_region) | The Digital Ocean region where you wish to deploy Kasm | `string` | `"nyc3"` | no |
| <a name="input_digital_ocean_token"></a> [digital\_ocean\_token](#input\_digital\_ocean\_token) | Authentication Token For Digital Ocean | `string` | n/a | yes |
| <a name="input_do_domain_name"></a> [do\_domain\_name](#input\_do\_domain\_name) | The domain name that users will use to access Kasm | `string` | n/a | yes |
| <a name="input_kasm_build_url"></a> [kasm\_build\_url](#input\_kasm\_build\_url) | The Kasm build file to install | `string` | `"https://kasm-static-content.s3.amazonaws.com/kasm_release_1.12.0.d4fd8a.tar.gz"` | no |
| <a name="input_project_name"></a> [project\_name](#input\_project\_name) | The name of the project/deployment/company eg (acme). | `string` | n/a | yes |
| <a name="input_ssh_key_fingerprints"></a> [ssh\_key\_fingerprints](#input\_ssh\_key\_fingerprints) | Keys used for sshing into kasm hosts | `list(string)` | n/a | yes |
| <a name="input_swap_size"></a> [swap\_size](#input\_swap\_size) | The amount of swap (in MB) to configure inside the compute instances | `number` | `2048` | no |
| <a name="input_user_password"></a> [user\_password](#input\_user\_password) | The default password to be used for the default user@kasm.local account. Only use alphanumeric characters | `string` | `"changeme"` | no |
| <a name="input_vpc_subnet_cidr"></a> [vpc\_subnet\_cidr](#input\_vpc\_subnet\_cidr) | VPC Subnet CIDR where you wish to deploy Kasm | `string` | `"10.0.0.0/24"` | no |
## Outputs
No outputs.
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

View file

@ -0,0 +1,64 @@
# module
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.0 |
| <a name="requirement_digitalocean"></a> [digitalocean](#requirement\_digitalocean) | ~> 2.0 |
## Providers
| Name | Version |
|------|---------|
| <a name="provider_digitalocean"></a> [digitalocean](#provider\_digitalocean) | 2.34.1 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [digitalocean_certificate.cert](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/certificate) | resource |
| [digitalocean_domain.default](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/domain) | resource |
| [digitalocean_droplet.kasm-server](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/droplet) | resource |
| [digitalocean_firewall.workspaces-fw](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/firewall) | resource |
| [digitalocean_loadbalancer.www-lb](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/loadbalancer) | resource |
| [digitalocean_project.project](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/project) | resource |
| [digitalocean_record.static](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/record) | resource |
| [digitalocean_tag.project](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/tag) | resource |
| [digitalocean_vpc.kasm_vpc](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/resources/vpc) | resource |
| [digitalocean_certificate.data-cert](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/data-sources/certificate) | data source |
| [digitalocean_domain.data-default](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/data-sources/domain) | data source |
| [digitalocean_droplet.data-kasm_server](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/data-sources/droplet) | data source |
| [digitalocean_tag.data-project](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/data-sources/tag) | data source |
| [digitalocean_vpc.data-kasm_vpc](https://registry.terraform.io/providers/digitalocean/digitalocean/latest/docs/data-sources/vpc) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_admin_password"></a> [admin\_password](#input\_admin\_password) | The default password to be used for the default admin@kasm.local account. Only use alphanumeric characters | `string` | n/a | yes |
| <a name="input_allow_kasm_web_cidrs"></a> [allow\_kasm\_web\_cidrs](#input\_allow\_kasm\_web\_cidrs) | CIDR notation of the bastion host allowed to SSH in to the machines | `list(string)` | n/a | yes |
| <a name="input_allow_ssh_cidrs"></a> [allow\_ssh\_cidrs](#input\_allow\_ssh\_cidrs) | List of Subnets in CIDR notation for hosts allowed to SSH | `list(string)` | n/a | yes |
| <a name="input_anywhere"></a> [anywhere](#input\_anywhere) | Anywhere route subnet | `list(string)` | <pre>[<br> "0.0.0.0/0",<br> "::/0"<br>]</pre> | no |
| <a name="input_digital_ocean_droplet_slug"></a> [digital\_ocean\_droplet\_slug](#input\_digital\_ocean\_droplet\_slug) | The Default Digital Ocean Droplet Slug: https://slugs.do-api.dev/ | `string` | n/a | yes |
| <a name="input_digital_ocean_image"></a> [digital\_ocean\_image](#input\_digital\_ocean\_image) | Default Image for Ubuntu LTS | `string` | n/a | yes |
| <a name="input_digital_ocean_region"></a> [digital\_ocean\_region](#input\_digital\_ocean\_region) | The Default Digital Ocean Region Slug: https://docs.digitalocean.com/products/platform/availability-matrix/ | `string` | n/a | yes |
| <a name="input_do_domain_name"></a> [do\_domain\_name](#input\_do\_domain\_name) | The domain name that users will use to access kasm | `string` | n/a | yes |
| <a name="input_kasm_build_url"></a> [kasm\_build\_url](#input\_kasm\_build\_url) | The Kasm build file to install | `string` | n/a | yes |
| <a name="input_project_name"></a> [project\_name](#input\_project\_name) | The name of the project/deployment/company eg (acme). Lower case all one word as this will be used in a domain name | `string` | n/a | yes |
| <a name="input_ssh_key_fingerprints"></a> [ssh\_key\_fingerprints](#input\_ssh\_key\_fingerprints) | Keys used for sshing into kasm hosts | `list(string)` | n/a | yes |
| <a name="input_swap_size"></a> [swap\_size](#input\_swap\_size) | The amount of swap (in MB) to configure inside the compute instances | `number` | n/a | yes |
| <a name="input_user_password"></a> [user\_password](#input\_user\_password) | The default password to be used for the default user@kasm.local account. Only use alphanumeric characters | `string` | n/a | yes |
| <a name="input_vpc_subnet_cidr"></a> [vpc\_subnet\_cidr](#input\_vpc\_subnet\_cidr) | VPC Subnet CIDR to deploy Kasm | `string` | n/a | yes |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_kasm_server_ip"></a> [kasm\_server\_ip](#output\_kasm\_server\_ip) | n/a |
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

View file

@ -1,7 +1,7 @@
resource "digitalocean_firewall" "workspaces-fw" {
name = "${var.project_name}-fw"
tags = ["${digitalocean_tag.project.id}"]
tags = [digitalocean_tag.project.id]
inbound_rule {
protocol = "tcp"

View file

@ -1,4 +1,6 @@
terraform {
required_version = "~> 1.0"
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"

View file

@ -1,4 +1,6 @@
terraform {
required_version = "~> 1.0"
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"

View file

@ -0,0 +1 @@
digital_ocean_token = ""

View file

@ -13,12 +13,12 @@ digital_ocean_droplet_slug = "s-2vcpu-4gb-intel"
swap_size = 2048
## Kasm passwords
user_password = "changeme"
admin_password = "changeme"
user_password = "changeme"
admin_password = "changeme"
## VM Access subnets
allow_ssh_cidrs = ["0.0.0.0/0"]
allow_kasm_web_cidrs = ["0.0.0.0/0"]
## Kasm download URL
kasm_build_url = "https://kasm-static-content.s3.amazonaws.com/kasm_release_1.13.0.002947.tar.gz"
kasm_build_url = "https://kasm-static-content.s3.amazonaws.com/kasm_release_1.14.0.3a7abb.tar.gz"

View file

@ -126,6 +126,7 @@ variable "allow_kasm_web_cidrs" {
variable "swap_size" {
description = "The amount of swap (in MB) to configure inside the compute instances"
type = number
default = 2048
validation {

View file

@ -34,48 +34,76 @@ Create an SSL certificate that matches the desired domain for the deployment. e.
terraform init
2. Open `settings.tfvars` and update the variables. The variable definitions, descriptions, and validation requirements can be found in `variables.tf`, or in the [table](#oci-terraform-variable-definitions) below.
3. Verify the configuration
terraform plan -var-file settings.tfvars
terraform plan
4. Deploy
terraform apply -var-file settings.tfvars
terraform apply
5. Login to the Deployment as an Admin via the domain defined e.g `https://kasm.contoso.com`. Single server installs download all workspaces images during the install process so it may take ~15 minutes for the server to fully come online.
# OCI Terraform Variable definitions
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Variable | Description | Variable type | Example |
|:--------:|-------------|---------------|---------|
| `tenancy_ocid` | The OCI Tenancy OCID | String | `"ocid1.tenancy.oc1..aaaaaaaaai06vvcguozt39d4ilmwtpdovl998wsxpyn0hjkab2kuh7z16po7"` |
| `compartment_ocid` | The OCI Compartment OCID | String | `"ocid1.compartment.oc1..aaaaaaaauepg1z967huiazuiwjt80rtbszp64x9oxaidkoi7wz0pgr950bzb"` |
| `region` | The OCI Region name | String | `"us-ashburn-1"` |
| `user_ocid` | The OCI User OCID | String | `"ocid1.user.oc1..aaaaaaaau3me8nojmdjrbj2vzfxeouscc1i7cf9w0aoy0iyv9b38t2y0a1ba"` |
| `fingerprint` | The OCI User API Key fingerprint | String | `"66:e5:d1:85:cd:ba:ca:6a:d0:76:86:ef:1c:11:63:97"` |
| `private_key_path` | The path for the API Key PEM encoded Private Key for the OCI User. ***NOTE:*** *Ensure the API Key contents are a valid PEM encoded RSA key file. You can tell this by ensuring that the value `-----BEGIN RSA PRIVATE KEY-----` is the first line in the key file. Otherwise, you can validate the key file by running the `openssl rsa -in oci-private-key.pem -check` command.* | String | `"./oci-private-key.pem"` |
| `project_name` | The name of the deployment (e.g dev, staging). A short single word of up to 15 characters. | String | `"kasm"` |
| `oci_domain_name` | The public Zone used for the dns entries. This must already exist in the OCI account. (e.g kasm.contoso.com). The deployment will be accessed via this zone name using https. | String | `"kasm.contoso.com"` |
| `letsencrypt_cert_support_email` | Email address to use for Terraform-generated Let's Encrypt SSL certificates | String | `"support@contoso.com"` |
| `letsencrypt_server_type` | SSL Server type for certificate generation. Valid options are staging, prod, and empty string (""). Refer to [SSL Certificate Options](#ssl-certificate-options) section of this document for more information. | String | "prod" |
| `kasm_ssl_crt_path` | Bring Your own Certificate - The file path fo the PEM encoded SSL Certificate file generated outside of Terraform. Copy/paste the contents of your generated SSL Certificate to the file designated in this path variable. | String | `"./kasm_ssl.crt"` |
| `kasm_ssl_key_path` | Bring Your own Certificate - The file path to the PEM encoded SSL Private Key file generated outside of Terraform. Copy/paste the contents of your generated SSL Private Key to the file designated in this path variable. | String | `"./kasm_ssl.key"` |
| `vcn_subnet_cidr` | The OCI VCN Subnet CIDR of the VCN where you wish to deploy Kasm | String | `"10.0.0.0/16"` |
| `ssh_authorized_keys` | The SSH Public key to be installed on the Kasm servers for SSH access | String | `"ssh-rsa some_base64_encoded_ssh_public_key_data"` |
| `instance_image_ocid` | The OCI Image OCID value of the OS to use. Kasm recommends using lates Ubuntu 20.04 LTS-Minimal for speed and efficiency. | String | `"ocid1.image.oc1.iad.aaaaaaaahiz6xym3a76xhwkmwmhrz6luyiehho7dpxpkphxhsq5q6z4m3nlq"` |
| `allow_ssh_cidrs` | A list of subnets in CIDR notation allowed to SSH into your kasm servers | List(String) | `["10.0.0.0/16","172.217.22.14/32"]` |
| `allow_web_cidrs` | A list of subnets in CIDR notation allowed Web access to your kasm servers | List(String) | `["0.0.0.0/0"]` |
| `admin_password` | The Kasm Administrative user login password. String from 12-30 characters in length with no special characters. | String | `"1qaz2wsx3EDC4RFV"` |
| `user_password` | A Kasm standard (non-administrator) user password. String from 12-30 characters in length with no special characters. | String | `"1qaz2wsx3EDC4RFV"` |
| `kasm_build_url` | The download URL for the desired Kasm Workspaces version. | String | `"https://kasm-static-content.s3.amazonaws.com/kasm_release_1.13.0.002947.tar.gz"` |
| `swap_size` | The amount of swap (in MB) to configure inside the Kasm servers. | Number | `2048` |
| `instance_shape` | The OCI instance shape to use for Kasm deployment. Kasm recommends using a Flex instance type. | String | `"VM.Standard.E4.Flex"` |
| `kasm_server_cpus` | The number of CPUs, memory in GB, and HDD size to use for Kasm WebApps. | Number | `4` |
| `kasm_server_memory` | The number of CPUs, memory in GB, and HDD size to use for the Kasm Database server. | Number | `8` |
| `kasm_server_hdd_size` | The number of CPUs, memory in GB, and HDD size to use for the Kasm Agent server(s). | Number | `120` |
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.0 |
| <a name="requirement_acme"></a> [acme](#requirement\_acme) | ~> 2.0 |
| <a name="requirement_oci"></a> [oci](#requirement\_oci) | ~> 5.0 |
| <a name="requirement_tls"></a> [tls](#requirement\_tls) | ~> 4.0 |
## Providers
No providers.
## Modules
| Name | Source | Version |
|------|--------|---------|
| <a name="module_kasm"></a> [kasm](#module\_kasm) | ./module | n/a |
## Resources
No resources.
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_admin_password"></a> [admin\_password](#input\_admin\_password) | The administrative user password. No special characters | `string` | n/a | yes |
| <a name="input_allow_ssh_cidrs"></a> [allow\_ssh\_cidrs](#input\_allow\_ssh\_cidrs) | The CIDR notation to allow SSH access to the systems. | `list(string)` | n/a | yes |
| <a name="input_allow_web_cidrs"></a> [allow\_web\_cidrs](#input\_allow\_web\_cidrs) | The CIDR notation to allow HTTPS access to the systems. | `list(string)` | n/a | yes |
| <a name="input_compartment_ocid"></a> [compartment\_ocid](#input\_compartment\_ocid) | The Compartment OCID | `string` | n/a | yes |
| <a name="input_fingerprint"></a> [fingerprint](#input\_fingerprint) | API Key Fingerprint | `string` | n/a | yes |
| <a name="input_instance_image_ocid"></a> [instance\_image\_ocid](#input\_instance\_image\_ocid) | The OCID for the instance image , such as ubuntu 20.04, to use. | `string` | n/a | yes |
| <a name="input_instance_shape"></a> [instance\_shape](#input\_instance\_shape) | The instance shape to use. Should be a Flex type. | `string` | n/a | yes |
| <a name="input_kasm_build_url"></a> [kasm\_build\_url](#input\_kasm\_build\_url) | The URL for the Kasm Workspaces build | `string` | n/a | yes |
| <a name="input_kasm_server_cpus"></a> [kasm\_server\_cpus](#input\_kasm\_server\_cpus) | The number of CPUs to configure for the Kasm instance | `number` | n/a | yes |
| <a name="input_kasm_server_hdd_size"></a> [kasm\_server\_hdd\_size](#input\_kasm\_server\_hdd\_size) | The size in GBs of the Kasm instance HDD | `number` | n/a | yes |
| <a name="input_kasm_server_memory"></a> [kasm\_server\_memory](#input\_kasm\_server\_memory) | The amount of memory to configure for the Kasm instance | `number` | n/a | yes |
| <a name="input_kasm_ssl_crt_path"></a> [kasm\_ssl\_crt\_path](#input\_kasm\_ssl\_crt\_path) | The file path to the PEM encoded SSL Certificate. Leave this empty if you are using Lets Encrypt to automatically generate your certificates. | `string` | `""` | no |
| <a name="input_kasm_ssl_key_path"></a> [kasm\_ssl\_key\_path](#input\_kasm\_ssl\_key\_path) | The file path to the PEM encoded SSL Certificate Key. Leave this empty if you are using Lets Encrypt to automatically generate your certificates. | `string` | `""` | no |
| <a name="input_letsencrypt_cert_support_email"></a> [letsencrypt\_cert\_support\_email](#input\_letsencrypt\_cert\_support\_email) | Email address to use for Let's Encrypt SSL certificates for OCI Deployment | `string` | `""` | no |
| <a name="input_letsencrypt_server_type"></a> [letsencrypt\_server\_type](#input\_letsencrypt\_server\_type) | SSL Server type to generate. Valid options are staging and prod, and prod certificates are limited to 5 certificates per week. | `string` | `""` | no |
| <a name="input_oci_domain_name"></a> [oci\_domain\_name](#input\_oci\_domain\_name) | The public Zone used for the dns entries. This must already exist in the OCI account. (e.g kasm.contoso.com). The deployment will be accessed via this zone name via https | `string` | n/a | yes |
| <a name="input_private_key_path"></a> [private\_key\_path](#input\_private\_key\_path) | The path to the OCI API Key PEM encoded Private Key | `string` | n/a | yes |
| <a name="input_project_name"></a> [project\_name](#input\_project\_name) | The name of the deployment (e.g dev, staging). A short single word | `string` | n/a | yes |
| <a name="input_region"></a> [region](#input\_region) | The OCI Region eg: (us-ashburn-1) | `string` | n/a | yes |
| <a name="input_ssh_authorized_keys"></a> [ssh\_authorized\_keys](#input\_ssh\_authorized\_keys) | The SSH Public Keys to be installed on the OCI compute instance | `string` | n/a | yes |
| <a name="input_swap_size"></a> [swap\_size](#input\_swap\_size) | The amount of swap (in MB) to configure inside the compute instances | `number` | n/a | yes |
| <a name="input_tenancy_ocid"></a> [tenancy\_ocid](#input\_tenancy\_ocid) | The Tenancy OCID. | `string` | n/a | yes |
| <a name="input_user_ocid"></a> [user\_ocid](#input\_user\_ocid) | The User OCID. | `string` | n/a | yes |
| <a name="input_user_password"></a> [user\_password](#input\_user\_password) | The standard (non administrator) user password. No special characters | `string` | n/a | yes |
| <a name="input_vcn_subnet_cidr"></a> [vcn\_subnet\_cidr](#input\_vcn\_subnet\_cidr) | VCN Subnet CIDR where you wish to deploy Kasm | `string` | n/a | yes |
## Outputs
No outputs.
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
# Detailed Terraform Deployment Diagram

View file

@ -0,0 +1,79 @@
# module
<!-- BEGINNING OF PRE-COMMIT-TERRAFORM DOCS HOOK -->
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.0 |
| <a name="requirement_acme"></a> [acme](#requirement\_acme) | ~> 2.0 |
| <a name="requirement_oci"></a> [oci](#requirement\_oci) | ~> 5.0 |
| <a name="requirement_tls"></a> [tls](#requirement\_tls) | ~> 4.0 |
## Providers
| Name | Version |
|------|---------|
| <a name="provider_acme"></a> [acme](#provider\_acme) | 2.20.0 |
| <a name="provider_oci"></a> [oci](#provider\_oci) | 5.28.0 |
| <a name="provider_tls"></a> [tls](#provider\_tls) | 4.0.5 |
## Modules
No modules.
## Resources
| Name | Type |
|------|------|
| [acme_certificate.certificate](https://registry.terraform.io/providers/vancluever/acme/latest/docs/resources/certificate) | resource |
| [acme_registration.registration](https://registry.terraform.io/providers/vancluever/acme/latest/docs/resources/registration) | resource |
| [oci_core_default_route_table.default_route_table](https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/core_default_route_table) | resource |
| [oci_core_instance.kasm_instance](https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/core_instance) | resource |
| [oci_core_internet_gateway.kasm_internet_gateway](https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/core_internet_gateway) | resource |
| [oci_core_security_list.allow_ssh](https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/core_security_list) | resource |
| [oci_core_security_list.allow_web](https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/core_security_list) | resource |
| [oci_core_subnet.kasm_subnet](https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/core_subnet) | resource |
| [oci_core_vcn.kasm_vcn](https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/core_vcn) | resource |
| [oci_dns_rrset.kasm_a_record](https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/dns_rrset) | resource |
| [tls_cert_request.kasm_certificate_request](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/cert_request) | resource |
| [tls_private_key.certificate_private_key](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/private_key) | resource |
| [tls_private_key.registration_private_key](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/private_key) | resource |
| [oci_dns_zones.kasm_dns_zone](https://registry.terraform.io/providers/oracle/oci/latest/docs/data-sources/dns_zones) | data source |
| [oci_identity_availability_domain.ad](https://registry.terraform.io/providers/oracle/oci/latest/docs/data-sources/identity_availability_domain) | data source |
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_admin_password"></a> [admin\_password](#input\_admin\_password) | The administrative user password. No special characters | `string` | n/a | yes |
| <a name="input_allow_ssh_cidrs"></a> [allow\_ssh\_cidrs](#input\_allow\_ssh\_cidrs) | The CIDR notation to allow SSH access to the systems. | `list(string)` | n/a | yes |
| <a name="input_allow_web_cidrs"></a> [allow\_web\_cidrs](#input\_allow\_web\_cidrs) | The CIDR notation to allow HTTPS access to the systems. | `list(string)` | n/a | yes |
| <a name="input_anywhere"></a> [anywhere](#input\_anywhere) | Anywhere route subnet | `string` | `"0.0.0.0/0"` | no |
| <a name="input_compartment_ocid"></a> [compartment\_ocid](#input\_compartment\_ocid) | The Compartment OCID | `string` | n/a | yes |
| <a name="input_fingerprint"></a> [fingerprint](#input\_fingerprint) | API Key Fingerprint | `string` | n/a | yes |
| <a name="input_instance_image_ocid"></a> [instance\_image\_ocid](#input\_instance\_image\_ocid) | The OCID for the instance image , such as ubuntu 20.04, to use. | `string` | n/a | yes |
| <a name="input_instance_shape"></a> [instance\_shape](#input\_instance\_shape) | The instance shape to use. Should be a Flex type. | `string` | n/a | yes |
| <a name="input_kasm_build_url"></a> [kasm\_build\_url](#input\_kasm\_build\_url) | The URL for the Kasm Workspaces build | `string` | n/a | yes |
| <a name="input_kasm_server_cpus"></a> [kasm\_server\_cpus](#input\_kasm\_server\_cpus) | The number of CPUs to configure for the Kasm instance | `number` | n/a | yes |
| <a name="input_kasm_server_hdd_size"></a> [kasm\_server\_hdd\_size](#input\_kasm\_server\_hdd\_size) | The size in GBs of the Kasm instance HDD | `number` | n/a | yes |
| <a name="input_kasm_server_memory"></a> [kasm\_server\_memory](#input\_kasm\_server\_memory) | The amount of memory to configure for the Kasm instance | `number` | n/a | yes |
| <a name="input_kasm_ssl_crt_path"></a> [kasm\_ssl\_crt\_path](#input\_kasm\_ssl\_crt\_path) | The file path to the PEM encoded SSL Certificate | `string` | n/a | yes |
| <a name="input_kasm_ssl_key_path"></a> [kasm\_ssl\_key\_path](#input\_kasm\_ssl\_key\_path) | The file path to the PEM encoded SSL Certificate Key | `string` | n/a | yes |
| <a name="input_letsencrypt_cert_support_email"></a> [letsencrypt\_cert\_support\_email](#input\_letsencrypt\_cert\_support\_email) | Email address to use for Let's Encrypt SSL certificates for OCI Deployment | `string` | n/a | yes |
| <a name="input_letsencrypt_server_type"></a> [letsencrypt\_server\_type](#input\_letsencrypt\_server\_type) | SSL Server type to generate. Valid options are staging, prod, and empty string. Prod certificates are limited to 5 per week per domain. | `string` | n/a | yes |
| <a name="input_oci_domain_name"></a> [oci\_domain\_name](#input\_oci\_domain\_name) | The public Zone used for the dns entries. This must already exist in the OCI account. (e.g kasm.contoso.com). The deployment will be accessed via this zone name via https | `string` | n/a | yes |
| <a name="input_private_key_path"></a> [private\_key\_path](#input\_private\_key\_path) | The path to the API Key PEM encoded Private Key | `string` | n/a | yes |
| <a name="input_project_name"></a> [project\_name](#input\_project\_name) | The name of the deployment (e.g dev, staging). A short single word | `string` | n/a | yes |
| <a name="input_region"></a> [region](#input\_region) | The OCI Region eg: (us-ashburn-1) | `string` | n/a | yes |
| <a name="input_ssh_authorized_keys"></a> [ssh\_authorized\_keys](#input\_ssh\_authorized\_keys) | The SSH Public Keys to be installed on the OCI compute instance | `string` | n/a | yes |
| <a name="input_swap_size"></a> [swap\_size](#input\_swap\_size) | The amount of swap (in MB) to configure inside the compute instances | `number` | n/a | yes |
| <a name="input_tenancy_ocid"></a> [tenancy\_ocid](#input\_tenancy\_ocid) | The Tenancy OCID. | `string` | n/a | yes |
| <a name="input_user_ocid"></a> [user\_ocid](#input\_user\_ocid) | The User OCID. | `string` | n/a | yes |
| <a name="input_user_password"></a> [user\_password](#input\_user\_password) | The standard (non administrator) user password. No special characters | `string` | n/a | yes |
| <a name="input_vcn_subnet_cidr"></a> [vcn\_subnet\_cidr](#input\_vcn\_subnet\_cidr) | VPC Subnet CIDR where you wish to deploy Kasm | `string` | n/a | yes |
## Outputs
No outputs.
<!-- END OF PRE-COMMIT-TERRAFORM DOCS HOOK -->

View file

@ -1,16 +1,18 @@
terraform {
required_version = "~> 1.0"
required_providers {
oci = {
source = "oracle/oci"
version = ">= 4.0.0"
version = "~> 5.0"
}
acme = {
source = "vancluever/acme"
version = ">= 2.0"
version = "~> 2.0"
}
tls = {
source = "hashicorp/tls"
version = ">= 4.0.0"
version = "~> 4.0"
}
}
}

View file

@ -1,16 +1,18 @@
terraform {
required_version = "~> 1.0"
required_providers {
oci = {
source = "oracle/oci"
version = ">= 4.0.0"
version = "~> 5.0"
}
acme = {
source = "vancluever/acme"
version = ">= 2.0"
version = "~> 2.0"
}
tls = {
source = "hashicorp/tls"
version = ">= 4.0.0"
version = "~> 4.0"
}
}
}

Some files were not shown because too many files have changed in this diff Show more