diff --git a/CHANGELOG.md b/CHANGELOG.md index b014c93..9b581e1 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,25 @@ +## v0.3.0 (September 30, 2023) + +FEATURES: +* Azure Private DNS module (terraform-zscc-private-dns-azure) + - add: deployment types base_1cc_zpa/base_cc_lb_zpa (greenfield/pov/test) with Azure Private DNS module integration + - add: conditional variable zpa_enabled for cc_lb (brownfield/prod) deployment for Azure Private DNS module integration + - add: zsec additions for new deployment options + domains adding to Private DNS Resolver Rule creation +* AzureRM Provider version bump to 3.74.x default. Support from 3.46.x to 3.74.x + +ENHANCEMENTS: +* Encryption at Host enabled by default + - add: encryption_at_host_enabled variable and default to true +* change: workload VM for greenfield deployments dns_servers to Azure DNS default +* add: AZURE_MANAGED_IDENTITY_CLIENT_ID field to userdata generation +* change: variable load_distribution set to Azure "Default" corresponding to None/5-tuple session persistency in the Azure Portal. +* change: name_prefix variable default to zscc + +BUG FIXES: +* refactor: terraform-zscc-network-azure to remove data source read dependencies +* add: variable probe_threshold for [Azure LB health probe fixes](https://learn.microsoft.com/en-us/azure/load-balancer/whats-new#known-issues:~:text=February%202020-,Known%20issues,-The%20product%20group) + + ## v0.2.0 (March 7, 2023) * Mininum Azure Provider upgrade from 2.x to 3.x (3.46.x) diff --git a/README.md b/README.md index 260e1d1..8803472 100755 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Zscaler Cloud Connector Azure Terraform Modules # **README for Azure Terraform** -This README serves as a quick start guide to deploy Zscaler Cloud Connector resources in Microsoft Azure using Terraform. To learn more about the resources created when deploying Cloud Connector with Terraform, see [Deployment Templates for Zscaler Cloud Connector](https://help.zscaler.com/cloud-connector/about-cloud-automation-scripts). +This README serves as a quick start guide to deploy Zscaler Cloud Connector resources in Microsoft Azure using Terraform. To learn more about the resources created when deploying Cloud Connector with Terraform, see [Deployment Templates for Zscaler Cloud Connector](https://help.zscaler.com/cloud-branch-connector/deployment-templates-zscaler-cloud-connector). ## **Azure Deployment Scripts for Terraform** @@ -20,7 +20,7 @@ Use this repository to create the deployment resources required to deploy and op Our Deployment scripts are leveraging Terraform v1.1.9 which includes full binary and provider support for macOS M1 chips, but any Terraform version 0.13.7 should be generally supported. -- provider registry.terraform.io/hashicorp/azurerm v3.46.x +- provider registry.terraform.io/hashicorp/azurerm v3.74.x (minimum 3.46.x) - provider registry.terraform.io/hashicorp/random v3.3.x - provider registry.terraform.io/hashicorp/local v2.2.x - provider registry.terraform.io/hashicorp/null v3.1.x @@ -30,37 +30,55 @@ Our Deployment scripts are leveraging Terraform v1.1.9 which includes full binar 1. Azure Subscription Id [link to Azure subscriptions](https://portal.azure.com/#blade/Microsoft_Azure_Billing/SubscriptionsBlade) 2. Have/Create a Service Principal. See: [https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal](https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal)). Then Collect: - 1. Application (client) ID - 2. Directory (tenant) ID - 3. Client Secret Value + - Application (client) ID + - Directory (tenant) ID + - Client Secret Value 3. Azure Region (e.g. westus2) where Cloud Connector resources are to be deployed 4. User-created Azure Managed Identity. Role Assignment: Network Contributor (If using a Custom Role, the minimum requirement is: Microsoft. Network/networkInterfaces/read) Scope: Subscription or Resource Group (where Cloud Connector VMs will be deployed) 5. Azure Vault URL with Zscaler Cloud Connector Credentials (E.g. [https://zscaler-cc-demo.vault.azure.net](https://zscaler-cc-demo.vault.azure.net/)) Add an access policy to the above Key Vault as below - 1. Secret Permissions: Get, List - 2. Select Principal: The Managed Identity created in the above step + - Secret Permissions: Get, List + - Select Principal: The Managed Identity created in the above step 6. Accept the Cloud Connector VM image terms for the Subscription(s) where Cloud Connector is to be deployed. This can be done via the Azure Portal, Cloud Shell or az cli / powershell with a valid admin user/service principal in the correct subscription where Cloud Connector is being deployed Run Command: `az vm image terms accept --urn zscaler1579058425289:zia_cloud_connector:zs_ser_gen1_cc_01:latest` +### Terraform client requirements +7. If executing Terraform via the "zsec" wrapper bash script, it is advised that you run from a MacOS or Linux workstation. Minimum installed application requirements to successfully from the script are: + - bash | curl | unzip | rm | cp | find | grep | sed + +

These can all be installed via your distribution app installer. ie: sudo apt install bash curl unzip

+ ### **Zscaler requirements** -1. A valid Zscaler Cloud Connector provisioning URL generated. This is done via the Cloud Connector portal (E.g. connector..net/login) -2. Zscaler Cloud Connector Credentials (api key, username, password) are stored in Azure Key Vault from step 5. +8. A valid Zscaler Cloud Connector provisioning URL generated. This is done via the Cloud Connector portal (E.g. connector..net/login) +9. Zscaler Cloud Connector Credentials (api key, username, password) are stored in Azure Key Vault from step 5. + +### *Host Disk Encryption* +To enable host encryption. You **must** subscribe to the feature on your azure account. Official Microsoft Documentation on how to enable this feature can be found [here](https://learn.microsoft.com/en-us/azure/virtual-machines/disks-enable-host-based-encryption-portal?tabs=azure-cli#prerequisites) + ## **Greenfield Deployments** -Use this if you are building an entire cluster from the ground up. These templates include a bastion host and test workloads and are designed for greenfield/POV testing. +Use this if you are building an entire cluster from the ground up. These templates include a bastion host and test workloads and are designed for greenfield/POV testing. See [Modules](modules/) for the Terraform configurations for greenfield deployment. ### **Starter Deployment Template** Use the [**Starter Deployment Template**](examples/base_1cc) to deploy your Cloud Connector in a new resource group and virtual network. +### **Starter Deployment Template with ZPA** + +Use the [**Starter Deployment Template with ZPA**](examples/base_1cc_zpa) to deploy your Cloud Connector in a new resource group and virtual network with Azure Private DNS Resolver capability. + ### **Starter Deployment Template with Load Balancer** Use the [**Starter Deployment Template with Load Balancer**](examples/base_cc_lb) to deploy your Cloud Connector in a new resource group and virtual network and to load balance traffic across multiple Cloud Connectors. Zscaler's recommended deployment method is Azure Standard Load Balancer. Azure Load Balancer distributes traffic across multiple Cloud Connectors and achieves high availability. +### **Starter Deployment Template with Load Balancer and ZPA** + +Use the [**Starter Deployment Template with Load Balancer and ZPA**](examples/base_cc_lb_zpa) to deploy your Cloud Connector in a new resource group and virtual network and to load balance traffic across multiple Cloud Connectors with Azure Private DNS Resolver capability. Zscaler's recommended deployment method is Azure Standard Load Balancer. Azure Load Balancer distributes traffic across multiple Cloud Connectors and achieves high availability. + ## **Brownfield Deployment** Brownfield deployment templates are most applicable for production deployments and have more customization options than a "base" deployment. They also do not include a bastion or workload hosts deployed. See [Modules](modules/) for the Terraform configurations for brownfield deployment. ### **Custom Deployment Template with Azure Load Balancer** -Use the [**Custom Deployment template with Azure Load Balancer**](examples/cc_lb) to deploy your Cloud Connector in a new or existing VNet and load balance traffic across multiple Cloud Connectors. Zscaler's recommended deployment method is Azure Load Balancer. Azure Load Balancer distributes traffic across multiple Cloud Connectors and achieves high availability. +Use the [**Custom Deployment template with Azure Load Balancer**](examples/cc_lb) to deploy your Cloud Connector in a new or existing VNet and load balance traffic across multiple Cloud Connectors. Zscaler's recommended deployment method is Azure Load Balancer. Azure Load Balancer distributes traffic across multiple Cloud Connectors and achieves high availability. Optional Azure Private DNS Resolver resource creation per variable zpa_enabled. diff --git a/examples/README.md b/examples/README.md index e8d091f..a7741b6 100644 --- a/examples/README.md +++ b/examples/README.md @@ -4,35 +4,41 @@ ## Prerequisites (You will be prompted for Azure application credentials and region during deployment) -### Azure Requirements -1. Azure Subscription Id -[link to Azure subscriptions](https://portal.azure.com/#blade/Microsoft_Azure_Billing/SubscriptionsBlade) -2. Have/Create a Service Principal. See: https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal). Then Collect: - 1. Application (client) ID - 2. Directory (tenant) ID - 3. Client Secret Value +### **Azure Requirements** + +1. Azure Subscription Id [link to Azure subscriptions](https://portal.azure.com/#blade/Microsoft_Azure_Billing/SubscriptionsBlade) +2. Have/Create a Service Principal. See: [https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal](https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal)). Then Collect: + - Application (client) ID + - Directory (tenant) ID + - Client Secret Value 3. Azure Region (e.g. westus2) where Cloud Connector resources are to be deployed -4. User created Azure Managed Identity. - Role Assignment: Network Contributor (If using a Custom Role, the minimum requirement is: Microsoft.Network/networkInterfaces/read) - Scope: Subscription or Resource Group (where Cloud Connector VMs will be deployed) -5. Azure Vault URL with Zscaler Cloud Connector Credentials (E.g. https://zscaler-cc-demo.vault.azure.net) - Add an access policy to the above Key Vault as below - 1. Secret Permissions: Get, List - 2. Select Principal: The Managed Identity created in the above step -6. Accept the Cloud Connector VM image terms for the Subscription(s) where Cloud Connector is to be deployed. This can be done via the Azure Portal, Cloud Shell or az cli / powershell with a valid admin user/service principal in the correct subscription where Cloud Connector is being deployed - Run Command: az vm image terms accept --urn zscaler1579058425289:zia_cloud_connector:zs_ser_gen1_cc_01:latest - -### Zscaler requirements -7. A valid Zscaler Cloud Connector provisioning URL generated. This is done via the Cloud Connector portal (E.g. connector..net/login) -8. Zscaler Cloud Connector Credentials (api key, username, password) are stored in Azure Key Vault from step 5. +4. User-created Azure Managed Identity. Role Assignment: Network Contributor (If using a Custom Role, the minimum requirement is: Microsoft. Network/networkInterfaces/read) Scope: Subscription or Resource Group (where Cloud Connector VMs will be deployed) +5. Azure Vault URL with Zscaler Cloud Connector Credentials (E.g. [https://zscaler-cc-demo.vault.azure.net](https://zscaler-cc-demo.vault.azure.net/)) Add an access policy to the above Key Vault as below + - Secret Permissions: Get, List + - Select Principal: The Managed Identity created in the above step +6. Accept the Cloud Connector VM image terms for the Subscription(s) where Cloud Connector is to be deployed. This can be done via the Azure Portal, Cloud Shell or az cli / powershell with a valid admin user/service principal in the correct subscription where Cloud Connector is being deployed Run Command: `az vm image terms accept --urn zscaler1579058425289:zia_cloud_connector:zs_ser_gen1_cc_01:latest` + +### Terraform client requirements +7. If executing Terraform via the "zsec" wrapper bash script, it is advised that you run from a MacOS or Linux workstation. Minimum installed application requirements to successfully from the script are: + - bash | curl | unzip | rm | cp | find | grep | sed + +

These can all be installed via your distribution app installer. ie: sudo apt install bash curl unzip

+ +### **Zscaler requirements** + +8. A valid Zscaler Cloud Connector provisioning URL generated. This is done via the Cloud Connector portal (E.g. connector..net/login) +9. Zscaler Cloud Connector Credentials (api key, username, password) are stored in Azure Key Vault from step 5. See: [Zscaler Cloud Cloud Connector Azure Deployment Guide](https://help.zscaler.com/cloud-connector/deploying-cloud-connector-microsoft-azure) for additional prerequisite provisioning steps. +### *Host Disk Encryption* +To enable host encryption. You **must** subscribe to the feature on your azure account. Official Microsoft Documentation on how to enable this feature can be found [here](https://learn.microsoft.com/en-us/azure/virtual-machines/disks-enable-host-based-encryption-portal?tabs=azure-cli#prerequisites) + ## Deploying the cluster (The automated tool can run only from MacOS and Linux. You can also upload all repo contents to the respective public cloud provider Cloud Shells and run directly from there). -**1. Greenfield Deployments** +**1. Test/Greenfield Deployments** (Use this if you are building an entire cluster from ground up. Particularly useful for a Customer Demo/PoC or dev-test environment) @@ -50,21 +56,25 @@ Optional: Edit the terraform.tfvars file under your desired deployment type (ie: - verify all resources that will be created/modified and enter "yes" to confirm ``` -**Greenfield Deployment Types:** +**Test/Greenfield Deployment Types:** ``` -Deployment Type: (base | base_cc | base_cc_lb ): +Deployment Type: (base | base_1cc | base_1cc_zpa | base_cc_lb | base_cc_lb_zpa): **base** - Creates: 1 Resource Group containing; 1 VNet w/ 2 subnets (bastion + workload); 1 Ubuntu server workload w/ 1 Network Interface + NSG; 1 Ubuntu Bastion Host w/ 1 PIP + 1 Network Interface + NSG; generates local key pair .pem file for ssh access. This does NOT deploy any actual Cloud Connectors. -**base_cc** - Base deployment + Creates 1 Cloud Connector private subnet; 1 Cloud Connector VM in availability set routing to NAT Gateway; workload private subnet route repointed to the service interface IP of Cloud Connector +**base_1cc** - Base deployment + Creates 1 Cloud Connector private subnet; 1 Cloud Connector VM in availability set routing to NAT Gateway; workload private subnet route repointed to the service interface IP of Cloud Connector + +**base_1cc_zpa** - Everything from base_1cc + Creates Azure Private DNS Resolver, Private DNS Resolver Ruleset, Private DNS Resolver rules based on the number of domains entered, Virtual Network Link for Ruleset in the Cloud Connector VNet, and an Outbound Endpoint in a dedicated Outbound DNS subnet with custom UDR default route to CC. + +**base_cc_lb** - Everything from base_1cc deployment + Creates 2 Cloud Connectors in availability set behind 1 Internal Azure Load Balancer; Number of Workload and Cloud Connectors deployed customizable within terraform.tfvars cc_count and vm_count variables -**base_cc_lb** - Everything from base_cc deployment + Creates 2 Cloud Connectors in availability set behind 1 Internal Azure Load Balancer; Number of Workload and Cloud Connectors deployed customizable within terraform.tfvars cc_count and vm_count variables +**base_cc_lb_zpa** - Everything from base_cc_lb + Creates Azure Private DNS Resolver, Private DNS Resolver Ruleset, Private DNS Resolver rules based on the number of domains entered, Virtual Network Link for Ruleset in the Cloud Connector VNet, and an Outbound Endpoint in a dedicated Outbound DNS subnet with custom UDR default route to CC LB VIP. ``` -**2. Brownfield Deployments** +**2. Prod/Brownfield Deployments** -(These templates would be most applicable for production deployments and have more customization options than a "base" deployments) +(These templates would be most applicable for production deployments and have more customization options than a "base" deployments). They also do not include a bastion or workload hosts deployed. ``` bash @@ -79,15 +89,15 @@ Optional: Edit the terraform.tfvars file under your desired deployment type (ie: - verify all resources that will be created/modified and enter "yes" to confirm ``` -**Brownfield Deployment Types** +**Prod/Brownfield Deployment Types** ``` -Deployment Type: (cc_lb_custom): -**cc_lb_custom** - Creates 1 Resource Group containing: 1 VNet w/ 1 CC subnet; 2 Cloud Connectors in availability set with 1 PIP; 1 NAT Gateway; Mgmt Network Interfaces + NSG, Service Network Interfaces + NSG; 1 Internal Azure LB; generates local key pair .pem file for ssh access. Number of Cloud Connectors deployed and ability to use existing resources (resource group(s), VNet/Subnets, PIP, NAT GW) customizable withing terraform.tfvars custom variables +Deployment Type: (cc_lb): +**cc_lb** - Creates 1 Resource Group containing: 1 VNet w/ 1 CC subnet; 2 Cloud Connectors in availability set with 1 PIP; 1 NAT Gateway; Mgmt Network Interfaces + NSG, Service Network Interfaces + NSG; 1 Internal Azure LB; generates local key pair .pem file for ssh access. Number of Cloud Connectors deployed and ability to use existing resources (resource group(s), VNet/Subnets, PIP, NAT GW) customizable withing terraform.tfvars custom variables -Deployment type cc_lb_custom provides numerous customization options within terraform.tfvars to enable/disable bring-your-own resources for +Deployment type cc_lb provides numerous customization options within terraform.tfvars to enable/disable bring-your-own resources for Cloud Connector deployment in existing environments. Custom paramaters include: BYO existing Resource Group, PIPs, NAT Gateways and associations, -VNet, and subnets +VNet, and subnets. Optional Azure Private DNS Resolver resource creation per variable zpa_enabled. ``` ## Destroying the cluster diff --git a/examples/base/README.md b/examples/base/README.md index b4536b7..f3cd751 100644 --- a/examples/base/README.md +++ b/examples/base/README.md @@ -40,7 +40,7 @@ From base directory execute: | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 0.13.7, < 2.0.0 | -| [azurerm](#requirement\_azurerm) | ~> 3.46.0 | +| [azurerm](#requirement\_azurerm) | >= 3.46, <= 3.74 | | [local](#requirement\_local) | ~> 2.2.0 | | [null](#requirement\_null) | ~> 3.1.0 | | [random](#requirement\_random) | ~> 3.3.0 | @@ -78,7 +78,7 @@ From base directory execute: | [arm\_location](#input\_arm\_location) | The Azure Region where resources are to be deployed | `string` | `"westus2"` | no | | [bastion\_nsg\_source\_prefix](#input\_bastion\_nsg\_source\_prefix) | User input for locking down SSH access to bastion to a specific IP or CIDR range. Defaults to any IP | `string` | `"*"` | no | | [environment](#input\_environment) | Customer defined environment tag. ie: Dev, QA, Prod, etc. | `string` | `"Development"` | no | -| [name\_prefix](#input\_name\_prefix) | The name prefix for all your resources | `string` | `"zsdemo"` | no | +| [name\_prefix](#input\_name\_prefix) | The name prefix for all your resources | `string` | `"zscc"` | no | | [network\_address\_space](#input\_network\_address\_space) | VNet IP CIDR Range. All subnet resources that might get created (public, workload, cloud connector) are derived from this /16 CIDR. If you require creating a VNet smaller than /16, you may need to explicitly define all other subnets via public\_subnets, workload\_subnets, cc\_subnets, and route53\_subnets variables | `string` | `"10.1.0.0/16"` | no | | [owner\_tag](#input\_owner\_tag) | Customer defined owner tag value. ie: Org, Dept, username, etc. | `string` | `"zscc-admin"` | no | | [public\_subnets](#input\_public\_subnets) | Public/Bastion Subnets to create in VNet. This is only required if you want to override the default subnets that this code creates via network\_address\_space variable. | `list(string)` | `null` | no | diff --git a/examples/base/main.tf b/examples/base/main.tf index a6aafba..09431ea 100755 --- a/examples/base/main.tf +++ b/examples/base/main.tf @@ -90,5 +90,5 @@ module "workload" { resource_group = module.network.resource_group_name subnet_id = module.network.workload_subnet_ids[0] ssh_key = tls_private_key.key.public_key_openssh - dns_servers = ["8.8.8.8", "8.8.4.4"] + dns_servers = [] } diff --git a/examples/base/outputs.tf b/examples/base/outputs.tf index 09878cf..d88cad2 100755 --- a/examples/base/outputs.tf +++ b/examples/base/outputs.tf @@ -1,6 +1,20 @@ locals { testbedconfig = < [terraform](#requirement\_terraform) | >= 0.13.7, < 2.0.0 | -| [azurerm](#requirement\_azurerm) | ~> 3.46.0 | +| [azurerm](#requirement\_azurerm) | >= 3.46, <= 3.74 | | [local](#requirement\_local) | ~> 2.2.0 | | [null](#requirement\_null) | ~> 3.1.0 | | [random](#requirement\_random) | ~> 3.3.0 | @@ -97,12 +97,13 @@ From base_1cc directory execute: | [ccvm\_image\_sku](#input\_ccvm\_image\_sku) | Azure Marketplace Cloud Connector Image SKU | `string` | `"zs_ser_gen1_cc_01"` | no | | [ccvm\_image\_version](#input\_ccvm\_image\_version) | Azure Marketplace Cloud Connector Image Version | `string` | `"latest"` | no | | [ccvm\_instance\_type](#input\_ccvm\_instance\_type) | Cloud Connector Image size | `string` | `"Standard_D2s_v3"` | no | +| [encryption\_at\_host\_enabled](#input\_encryption\_at\_host\_enabled) | User input for enabling or disabling host encryption | `bool` | `true` | no | | [env\_subscription\_id](#input\_env\_subscription\_id) | Azure Subscription ID where resources are to be deployed in | `string` | n/a | yes | | [environment](#input\_environment) | Customer defined environment tag. ie: Dev, QA, Prod, etc. | `string` | `"Development"` | no | | [http\_probe\_port](#input\_http\_probe\_port) | Port number for Cloud Connector cloud init to enable listener port for HTTP probe from Azure LB | `number` | `50000` | no | | [lb\_enabled](#input\_lb\_enabled) | Default true. Only relevant for 'base' deployments. Configure Workload Route Table to default route next hop to the CC Load Balancer IP passed from var.lb\_frontend\_ip. If false, default route next hop directly to the CC Service IP passed from var.cc\_service\_ip | `bool` | `false` | no | | [managed\_identity\_subscription\_id](#input\_managed\_identity\_subscription\_id) | Azure Subscription ID where the User Managed Identity resource exists. Only required if this Subscription ID is different than env\_subscription\_id | `string` | `null` | no | -| [name\_prefix](#input\_name\_prefix) | The name prefix for all your resources | `string` | `"zsdemo"` | no | +| [name\_prefix](#input\_name\_prefix) | The name prefix for all your resources | `string` | `"zscc"` | no | | [network\_address\_space](#input\_network\_address\_space) | VNet IP CIDR Range. All subnet resources that might get created (public, workload, cloud connector) are derived from this /16 CIDR. If you require creating a VNet smaller than /16, you may need to explicitly define all other subnets via public\_subnets, workload\_subnets, cc\_subnets, and route53\_subnets variables | `string` | `"10.1.0.0/16"` | no | | [owner\_tag](#input\_owner\_tag) | Customer defined owner tag value. ie: Org, Dept, username, etc. | `string` | `"zscc-admin"` | no | | [public\_subnets](#input\_public\_subnets) | Public/Bastion Subnets to create in VNet. This is only required if you want to override the default subnets that this code creates via network\_address\_space variable. | `list(string)` | `null` | no | diff --git a/examples/base_1cc/main.tf b/examples/base_1cc/main.tf index 342fe91..f2aa430 100755 --- a/examples/base_1cc/main.tf +++ b/examples/base_1cc/main.tf @@ -93,7 +93,7 @@ module "workload" { resource_group = module.network.resource_group_name subnet_id = module.network.workload_subnet_ids[0] ssh_key = tls_private_key.key.public_key_openssh - dns_servers = ["185.46.212.88", "185.46.212.89"] + dns_servers = [] } @@ -111,6 +111,7 @@ locals { CC_URL=${var.cc_vm_prov_url} AZURE_VAULT_URL=${var.azure_vault_url} HTTP_PROBE_PORT=${var.http_probe_port} +AZURE_MANAGED_IDENTITY_CLIENT_ID=${module.cc_identity.managed_identity_client_id} USERDATA } @@ -144,6 +145,7 @@ module "cc_vm" { mgmt_nsg_id = module.cc_nsg.mgmt_nsg_id service_nsg_id = module.cc_nsg.service_nsg_id accelerated_networking_enabled = var.accelerated_networking_enabled + encryption_at_host_enabled = var.encryption_at_host_enabled depends_on = [ local_file.user_data_file, diff --git a/examples/base_1cc/outputs.tf b/examples/base_1cc/outputs.tf index 1eafb1b..16c50a2 100755 --- a/examples/base_1cc/outputs.tf +++ b/examples/base_1cc/outputs.tf @@ -1,6 +1,20 @@ locals { testbedconfig = < zones. ## Not configurable for base or base_1cc deployment types. (All others - Default: 2) ## E.g. cc_count set to 4 and 2 zones set ['1","2"] will create 2x CCs in AZ1 and 2x CCs in AZ2 #cc_count = 2 - -## 11. By default, no zones are specified in any resource creation meaning they are either auto-assigned by Azure +## 13. By default, no zones are specified in any resource creation meaning they are either auto-assigned by Azure ## (Virtual Machines and NAT Gateways) or Zone-Redundant (Public IP) based on whatever default configuration is. ## Setting this value to true will do the following: ## 1. will create zonal NAT Gateway resources in order of the zones [1-3] specified in zones variable. 1x per zone @@ -102,8 +103,7 @@ #zones_enabled = true - -## 12. By default, this variable is used as a count (1) for resource creation of Public IP, NAT Gateway, and CC Subnets. +## 14. By default, this variable is used as a count (1) for resource creation of Public IP, NAT Gateway, and CC Subnets. ## This should only be modified if zones_enabled is also set to true ## Doing so will change the default zone aware configuration for the 3 aforementioned resources with the values specified ## @@ -118,8 +118,7 @@ #zones = ["1","2"] #zones = ["1","2","3"] - -## 13. Network Configuration: +## 15. Network Configuration: ## IPv4 CIDR configured with VNet creation. All Subnet resources (Workload, Public, and Cloud Connector) will be created based off this prefix ## /24 subnets are created assuming this cidr is a /16. If you require creating a VNet smaller than /16, you may need to explicitly define all other @@ -143,24 +142,29 @@ #workloads_subnets = ["10.x.y.z/24","10.x.y.z/24"] #cc_subnets = ["10.x.y.z/24","10.x.y.z/24"] - -## 14. Number of Workload VMs to be provisioned in the workload subnet. Only limitation is available IP space +## 16. Number of Workload VMs to be provisioned in the workload subnet. Only limitation is available IP space ## in subnet configuration. Only applicable for "base" deployment types. Default workload subnet is /24 so 250 max #workload_count = 2 - -## 15. Tag attribute "Owner" assigned to all resoure creation. (Default: "zscc-admin") +## 17. Tag attribute "Owner" assigned to all resoure creation. (Default: "zscc-admin") #owner_tag = "username@company.com" - -## 16. Tag attribute "Environment" assigned to all resources created. (Default: "Development") +## 18. Tag attribute "Environment" assigned to all resources created. (Default: "Development") #environment = "Development" - -## 17. By default, this script will apply 1 Network Security Group per Cloud Connector instance. +## 19. By default, this script will apply 1 Network Security Group per Cloud Connector instance. ## Uncomment if you want to use the same Network Security Group for ALL Cloud Connectors (true or false. Default: false) #reuse_nsg = true + +## 20. By default, Host encryption is enabled for Cloud Connector VMs. This does require the EncryptionAtHost feature +## enabled for your subscription though first. +## You can verify this by following the Azure Prerequisites guide here: +## https://learn.microsoft.com/en-us/azure/virtual-machines/linux/disks-enable-host-based-encryption-cli#prerequisites +## +## Uncomment if you want to not enable this VM setting + +#encryption_at_host_enabled = false diff --git a/examples/base_1cc/variables.tf b/examples/base_1cc/variables.tf index 0738eb1..7626071 100755 --- a/examples/base_1cc/variables.tf +++ b/examples/base_1cc/variables.tf @@ -13,7 +13,11 @@ variable "arm_location" { variable "name_prefix" { type = string description = "The name prefix for all your resources" - default = "zsdemo" + default = "zscc" + validation { + condition = length(var.name_prefix) <= 12 + error_message = "Variable name_prefix must be 12 or less characters." + } } variable "network_address_space" { @@ -226,3 +230,9 @@ variable "lb_enabled" { description = "Default true. Only relevant for 'base' deployments. Configure Workload Route Table to default route next hop to the CC Load Balancer IP passed from var.lb_frontend_ip. If false, default route next hop directly to the CC Service IP passed from var.cc_service_ip" default = false } + +variable "encryption_at_host_enabled" { + type = bool + description = "User input for enabling or disabling host encryption" + default = true +} diff --git a/examples/base_1cc/versions.tf b/examples/base_1cc/versions.tf index e97eecf..82f3301 100755 --- a/examples/base_1cc/versions.tf +++ b/examples/base_1cc/versions.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "~> 3.46.0" + version = ">= 3.46, <= 3.74" } random = { source = "hashicorp/random" diff --git a/examples/base_1cc_zpa/README.md b/examples/base_1cc_zpa/README.md new file mode 100644 index 0000000..7fd7fe2 --- /dev/null +++ b/examples/base_1cc_zpa/README.md @@ -0,0 +1,129 @@ +# Zscaler "Base_1cc_zpa" deployment type + +This deployment type is intended for greenfield/pov/lab purposes. It will deploy a fully functioning sandbox environment in a new Resource Group/VNet with test workload VMs. Full set of resources provisioned listed below, but this will effectively create all network infrastructure dependencies for an Azure environment. Everything from "Base" deployment type (Creates 1 new Resource Group; 1 VNet with 1 public subnet and 1 private/workload subnet; 1 Centos server workload in the private subnet; 1 Bastion Host in the public subnet assigned a Public IP; and generates local key pair .pem file for ssh access).
+ +Additionally: Creates 1 Cloud Connector private subnet associated to a NAT Gateway; 1 Cloud Connector VM; and workload private subnet UDR repointed to service interface IP of Cloud Connector; Private DNS Resolver, Private DNS Resolver Ruleset, Private DNS Resolver rules based on the number of domains entered, Virtual Network Link for Ruleset, and Outbound Endpoint in a dedicated Outbound DNS subnet. + +## Caveats/Considerations +- WSL2 DNS bug: If you are trying to run these Azure terraform deployments specifically from a Windows WSL2 instance like Ubuntu and receive an error containing a message similar to this "dial tcp: lookup management.azure.com on 172.21.240.1:53: cannot unmarshal DNS message" please refer here for a WSL2 resolv.conf fix. https://github.com/microsoft/WSL/issues/5420#issuecomment-646479747. +## How to deploy: + +### Option 1 (guided): +From the examples directory, run the zsec bash script that walks to all required inputs. +- ./zsec up +- enter "greenfield" +- enter "base_1cc_zpa" +- follow the remainder of the authentication and configuration input prompts. +- script will detect client operating system and download/run a specific version of terraform in a temporary bin directory +- inputs will be validated and terraform init/apply will automatically exectute. +- verify all resources that will be created/modified and enter "yes" to confirm + +### Option 2 (manual): +Modify/populate any required variable input values in base_1cc_zpa/terraform.tfvars file and save. + +From base_1cc_zpa directory execute: +- terraform init +- terraform apply + +## How to destroy: + +### Option 1 (guided): +From the examples directory, run the zsec bash script that walks to all required inputs. +- ./zsec destroy + +### Option 2 (manual): +From base_1cc_zpa directory execute: +- terraform destroy + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 0.13.7, < 2.0.0 | +| [azurerm](#requirement\_azurerm) | >= 3.46, <= 3.74 | +| [local](#requirement\_local) | ~> 2.2.0 | +| [null](#requirement\_null) | ~> 3.1.0 | +| [random](#requirement\_random) | ~> 3.3.0 | +| [tls](#requirement\_tls) | ~> 3.4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [azurerm](#provider\_azurerm) | >= 3.46, <= 3.74 | +| [local](#provider\_local) | ~> 2.2.0 | +| [null](#provider\_null) | ~> 3.1.0 | +| [random](#provider\_random) | ~> 3.3.0 | +| [tls](#provider\_tls) | ~> 3.4.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [bastion](#module\_bastion) | ../../modules/terraform-zscc-bastion-azure | n/a | +| [cc\_identity](#module\_cc\_identity) | ../../modules/terraform-zscc-identity-azure | n/a | +| [cc\_nsg](#module\_cc\_nsg) | ../../modules/terraform-zscc-nsg-azure | n/a | +| [cc\_vm](#module\_cc\_vm) | ../../modules/terraform-zscc-ccvm-azure | n/a | +| [network](#module\_network) | ../../modules/terraform-zscc-network-azure | n/a | +| [private\_dns](#module\_private\_dns) | ../../modules/terraform-zscc-private-dns-azure | n/a | +| [workload](#module\_workload) | ../../modules/terraform-zscc-workload-azure | n/a | + +## Resources + +| Name | Type | +|------|------| +| [azurerm_private_dns_resolver_virtual_network_link.dns_vnet_link](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_resolver_virtual_network_link) | resource | +| [local_file.private_key](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [local_file.testbed](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [local_file.user_data_file](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [null_resource.cc_error_checker](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [random_string.suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource | +| [tls_private_key.key](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/private_key) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [accelerated\_networking\_enabled](#input\_accelerated\_networking\_enabled) | Enable/Disable accelerated networking support on all Cloud Connector service interfaces | `bool` | `true` | no | +| [arm\_location](#input\_arm\_location) | The Azure Region where resources are to be deployed | `string` | `"westus2"` | no | +| [azure\_vault\_url](#input\_azure\_vault\_url) | Azure Vault URL | `string` | n/a | yes | +| [bastion\_nsg\_source\_prefix](#input\_bastion\_nsg\_source\_prefix) | user input for locking down SSH access to bastion to a specific IP or CIDR range | `string` | `"*"` | no | +| [cc\_count](#input\_cc\_count) | The number of Cloud Connectors to deploy. Validation assumes max for /24 subnet but could be smaller or larger as long as subnet can accommodate | `number` | `1` | no | +| [cc\_instance\_size](#input\_cc\_instance\_size) | Cloud Connector Instance size. Determined by and needs to match the Cloud Connector Portal provisioning template configuration | `string` | `"small"` | no | +| [cc\_subnets](#input\_cc\_subnets) | Cloud Connector Subnets to create in VNet. This is only required if you want to override the default subnets that this code creates via network\_address\_space variable. | `list(string)` | `null` | no | +| [cc\_vm\_managed\_identity\_name](#input\_cc\_vm\_managed\_identity\_name) | Azure Managed Identity name to attach to the CC VM. E.g zspreview-66117-mi | `string` | n/a | yes | +| [cc\_vm\_managed\_identity\_rg](#input\_cc\_vm\_managed\_identity\_rg) | Resource Group of the Azure Managed Identity name to attach to the CC VM. E.g. edgeconnector\_rg\_1 | `string` | n/a | yes | +| [cc\_vm\_prov\_url](#input\_cc\_vm\_prov\_url) | Zscaler Cloud Connector Provisioning URL | `string` | n/a | yes | +| [ccvm\_image\_offer](#input\_ccvm\_image\_offer) | Azure Marketplace Cloud Connector Image Offer | `string` | `"zia_cloud_connector"` | no | +| [ccvm\_image\_publisher](#input\_ccvm\_image\_publisher) | Azure Marketplace Cloud Connector Image Publisher | `string` | `"zscaler1579058425289"` | no | +| [ccvm\_image\_sku](#input\_ccvm\_image\_sku) | Azure Marketplace Cloud Connector Image SKU | `string` | `"zs_ser_gen1_cc_01"` | no | +| [ccvm\_image\_version](#input\_ccvm\_image\_version) | Azure Marketplace Cloud Connector Image Version | `string` | `"latest"` | no | +| [ccvm\_instance\_type](#input\_ccvm\_instance\_type) | Cloud Connector Image size | `string` | `"Standard_D2s_v3"` | no | +| [domain\_names](#input\_domain\_names) | Domain names fqdn/wildcard to have Azure Private DNS redirect DNS requests to Cloud Connector | `map(any)` | n/a | yes | +| [encryption\_at\_host\_enabled](#input\_encryption\_at\_host\_enabled) | User input for enabling or disabling host encryption | `bool` | `true` | no | +| [env\_subscription\_id](#input\_env\_subscription\_id) | Azure Subscription ID where resources are to be deployed in | `string` | n/a | yes | +| [environment](#input\_environment) | Customer defined environment tag. ie: Dev, QA, Prod, etc. | `string` | `"Development"` | no | +| [http\_probe\_port](#input\_http\_probe\_port) | Port number for Cloud Connector cloud init to enable listener port for HTTP probe from Azure LB | `number` | `50000` | no | +| [lb\_enabled](#input\_lb\_enabled) | Default true. Only relevant for 'base' deployments. Configure Workload Route Table to default route next hop to the CC Load Balancer IP passed from var.lb\_frontend\_ip. If false, default route next hop directly to the CC Service IP passed from var.cc\_service\_ip | `bool` | `false` | no | +| [managed\_identity\_subscription\_id](#input\_managed\_identity\_subscription\_id) | Azure Subscription ID where the User Managed Identity resource exists. Only required if this Subscription ID is different than env\_subscription\_id | `string` | `null` | no | +| [name\_prefix](#input\_name\_prefix) | The name prefix for all your resources | `string` | `"zscc"` | no | +| [network\_address\_space](#input\_network\_address\_space) | VNet IP CIDR Range. All subnet resources that might get created (public, workload, cloud connector) are derived from this /16 CIDR. If you require creating a VNet smaller than /16, you may need to explicitly define all other subnets via public\_subnets, workload\_subnets, cc\_subnets, and route53\_subnets variables | `string` | `"10.1.0.0/16"` | no | +| [owner\_tag](#input\_owner\_tag) | Customer defined owner tag value. ie: Org, Dept, username, etc. | `string` | `"zscc-admin"` | no | +| [private\_dns\_subnet](#input\_private\_dns\_subnet) | Private DNS Resolver Outbound Endpoint Subnet to create in VNet. This is only required if you want to override the default subnet that this code creates via network\_address\_space variable. | `string` | `null` | no | +| [public\_subnets](#input\_public\_subnets) | Public/Bastion Subnets to create in VNet. This is only required if you want to override the default subnets that this code creates via network\_address\_space variable. | `list(string)` | `null` | no | +| [reuse\_nsg](#input\_reuse\_nsg) | Specifies whether the NSG module should create 1:1 network security groups per instance or 1 network security group for all instances | `bool` | `"false"` | no | +| [target\_address](#input\_target\_address) | Azure DNS queries will be conditionally forwarded to these target IP addresses. Default are a pair of Zscaler Global VIP addresses | `list(string)` |
[
"185.46.212.88",
"185.46.212.89"
]
| no | +| [tls\_key\_algorithm](#input\_tls\_key\_algorithm) | algorithm for tls\_private\_key resource | `string` | `"RSA"` | no | +| [workload\_count](#input\_workload\_count) | The number of Workload VMs to deploy | `number` | `1` | no | +| [workloads\_subnets](#input\_workloads\_subnets) | Workload Subnets to create in VNet. This is only required if you want to override the default subnets that this code creates via network\_address\_space variable. | `list(string)` | `null` | no | +| [zones](#input\_zones) | Specify which availability zone(s) to deploy VM resources in if zones\_enabled variable is set to true | `list(string)` |
[
"1"
]
| no | +| [zones\_enabled](#input\_zones\_enabled) | Determine whether to provision Cloud Connector VMs explicitly in defined zones (if supported by the Azure region provided in the location variable). If left false, Azure will automatically choose a zone and module will create an availability set resource instead for VM fault tolerance | `bool` | `false` | no | +| [zpa\_enabled](#input\_zpa\_enabled) | Configure Azure Private DNS Outbound subnet, Resolvers, Rulesets/Rules, and Outbound Endpoint ZPA DNS redirection | `bool` | `true` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [testbedconfig](#output\_testbedconfig) | Azure Testbed results | + diff --git a/examples/base_1cc_zpa/backend.tf b/examples/base_1cc_zpa/backend.tf new file mode 100755 index 0000000..b739715 --- /dev/null +++ b/examples/base_1cc_zpa/backend.tf @@ -0,0 +1,5 @@ +terraform { + backend "local" { + path = "../terraform.tfstate" + } +} diff --git a/examples/base_1cc_zpa/main.tf b/examples/base_1cc_zpa/main.tf new file mode 100755 index 0000000..a0a116c --- /dev/null +++ b/examples/base_1cc_zpa/main.tf @@ -0,0 +1,239 @@ +################################################################################ +# Generate a unique random string for resource name assignment and key pair +################################################################################ +resource "random_string" "suffix" { + length = 8 + upper = false + special = false +} + + +################################################################################ +# Map default tags with values to be assigned to all tagged resources +################################################################################ +locals { + global_tags = { + Owner = var.owner_tag + ManagedBy = "terraform" + Vendor = "Zscaler" + Environment = var.environment + } +} + + +################################################################################ +# The following lines generates a new SSH key pair and stores the PEM file +# locally. The public key output is used as the instance_key passed variable +# to the vm modules for admin_ssh_key public_key authentication. +# This is not recommended for production deployments. Please consider modifying +# to pass your own custom public key file located in a secure location. +################################################################################ +# private key for login +resource "tls_private_key" "key" { + algorithm = var.tls_key_algorithm +} + +# write private key to local pem file +resource "local_file" "private_key" { + content = tls_private_key.key.private_key_pem + filename = "../${var.name_prefix}-key-${random_string.suffix.result}.pem" + file_permission = "0600" +} + + +################################################################################ +# 1. Create/reference all network infrastructure resource dependencies for all +# child modules (Resource Group, VNet, Subnets, NAT Gateway, Route Tables) +################################################################################ +module "network" { + source = "../../modules/terraform-zscc-network-azure" + name_prefix = var.name_prefix + resource_tag = random_string.suffix.result + global_tags = local.global_tags + location = var.arm_location + network_address_space = var.network_address_space + cc_subnets = var.cc_subnets + workloads_subnets = var.workloads_subnets + public_subnets = var.public_subnets + private_dns_subnet = var.private_dns_subnet + zones_enabled = var.zones_enabled + zones = var.zones + cc_service_ip = module.cc_vm.service_ip + workloads_enabled = true + bastion_enabled = true + lb_enabled = var.lb_enabled + zpa_enabled = var.zpa_enabled +} + + +################################################################################ +# 2. Create Bastion Host for workload and CC SSH jump access +################################################################################ +module "bastion" { + source = "../../modules/terraform-zscc-bastion-azure" + location = var.arm_location + name_prefix = var.name_prefix + resource_tag = random_string.suffix.result + global_tags = local.global_tags + resource_group = module.network.resource_group_name + public_subnet_id = module.network.bastion_subnet_ids[0] + ssh_key = tls_private_key.key.public_key_openssh + bastion_nsg_source_prefix = var.bastion_nsg_source_prefix +} + + +################################################################################ +# 3. Create Workload Hosts to test traffic connectivity through CC +################################################################################ +module "workload" { + source = "../../modules/terraform-zscc-workload-azure" + workload_count = var.workload_count + location = var.arm_location + name_prefix = var.name_prefix + resource_tag = random_string.suffix.result + global_tags = local.global_tags + resource_group = module.network.resource_group_name + subnet_id = module.network.workload_subnet_ids[0] + ssh_key = tls_private_key.key.public_key_openssh + dns_servers = [] +} + + +################################################################################ +# 4. Create specified number of CC VMs per cc_count by default in an +# availability set for Azure Data Center fault tolerance. Optionally, deployed +# CCs can automatically span equally across designated availabilty zones +# if enabled via "zones_enabled" and "zones" variables. E.g. cc_count set to +# 4 and 2 zones ['1","2"] will create 2x CCs in AZ1 and 2x CCs in AZ2 +################################################################################ +# Create the user_data file with necessary bootstrap variables for Cloud Connector registration +locals { + userdata = <> ../errorlog.txt +EOF + } +} diff --git a/examples/base_1cc_zpa/outputs.tf b/examples/base_1cc_zpa/outputs.tf new file mode 100755 index 0000000..3527db3 --- /dev/null +++ b/examples/base_1cc_zpa/outputs.tf @@ -0,0 +1,67 @@ +locals { + + testbedconfig = < zones. +## Not configurable for base or base_1cc deployment types. (All others - Default: 2) +## E.g. cc_count set to 4 and 2 zones set ['1","2"] will create 2x CCs in AZ1 and 2x CCs in AZ2 + +#cc_count = 2 + +## 13. By default, no zones are specified in any resource creation meaning they are either auto-assigned by Azure +## (Virtual Machines and NAT Gateways) or Zone-Redundant (Public IP) based on whatever default configuration is. +## Setting this value to true will do the following: +## 1. will create zonal NAT Gateway resources in order of the zones [1-3] specified in zones variable. 1x per zone +## 2. will NOT create availability set resource nor associate Cloud Connector VMs to one +## 3. will create zonal Cloud Connector Virtual Machine appliances looping through and alternating per the order of the zones +## [1-3] specified in the zones variable AND total number of Cloud Connectors specified in cc_count variable. +## (Default: false) + +#zones_enabled = true + +## 14. By default, this variable is used as a count (1) for resource creation of Public IP, NAT Gateway, and CC Subnets. +## This should only be modified if zones_enabled is also set to true +## Doing so will change the default zone aware configuration for the 3 aforementioned resources with the values specified +## +## Use case: Define zone numbers "1" and "2". This will create 2x Public IPs (one in zone 1; the other in zone 2), +## 2x NAT Gateways (one in zone 1; the other in zone 2), associate the zone 1 PIP w/ zone 1 NAT GW and the zone 2 +## PIP w/ zone 2 NAT GW, create 2x CC Subnets and associate subnet 1 w/ zone 1 NAT GW and subnet 2 w/ zone 2 NAT GW, +## then each CC created will be assigned a zone in the subnet corresponding to the same zone of the NAT GW and PIP associated. + +## Uncomment one of the desired zones configuration below. + +#zones = ["1"] +#zones = ["1","2"] +#zones = ["1","2","3"] + +## 15. Network Configuration: + +## IPv4 CIDR configured with VNet creation. All Subnet resources (Workload, Public, and Cloud Connector) will be created based off this prefix +## /24 subnets are created assuming this cidr is a /16. If you require creating a VNet smaller than /16, you may need to explicitly define all other +## subnets via public_subnets, workload_subnets, cc_subnets and private_dns_subnet variables (Default: "10.1.0.0/16") + +## Note: This variable only applies if you let Terraform create a new VNet. Custom deployment with byo_vnet enabled will ignore this + +#network_address_space = "10.1.0.0/16" + +## Subnet space. (Minimum /28 required. Default is null). If you do not specify subnets, they will automatically be assigned based on the default cidrsubnet +## creation within the VNet address_prefix block. Uncomment and modify if byo_vnet is set to true but byo_subnets is left false meaning you want terraform to create +## NEW subnets in that existing VNet. OR if you choose to modify the network_address_space from the default /16 so a smaller CIDR, you may need to edit the below variables +## to accommodate that address space. + +## ***** Note ***** +## It does not matter how many subnets you specify here. this script will only create in order 1 or as many as defined in the zones variable +## Default/Minumum: 1 - Maximum: 3 +## Example: If you change network_address_space to "10.2.0.0/24", set below variables to cidrs that fit in that /24 like cc_subnets = ["10.2.0.0/27","10.2.0.32/27"] etc. + +#public_subnets = ["10.x.y.z/24","10.x.y.z/24"] +#workloads_subnets = ["10.x.y.z/24","10.x.y.z/24"] +#cc_subnets = ["10.x.y.z/24","10.x.y.z/24"] +#private_dns_subnet = "10.x.y.z/28" + +## 16. Number of Workload VMs to be provisioned in the workload subnet. Only limitation is available IP space +## in subnet configuration. Only applicable for "base" deployment types. Default workload subnet is /24 so 250 max + +#workload_count = 2 + +## 17. Tag attribute "Owner" assigned to all resoure creation. (Default: "zscc-admin") + +#owner_tag = "username@company.com" + +## 18. Tag attribute "Environment" assigned to all resources created. (Default: "Development") + +#environment = "Development" + +## 19. By default, this script will apply 1 Network Security Group per Cloud Connector instance. +## Uncomment if you want to use the same Network Security Group for ALL Cloud Connectors (true or false. Default: false) + +#reuse_nsg = true + +## 20. By default, Host encryption is enabled for Cloud Connector VMs. This does require the EncryptionAtHost feature +## enabled for your subscription though first. +## You can verify this by following the Azure Prerequisites guide here: +## https://learn.microsoft.com/en-us/azure/virtual-machines/linux/disks-enable-host-based-encryption-cli#prerequisites +## +## Uncomment if you want to not enable this VM setting + +#encryption_at_host_enabled = false + + +##################################################################################################################### +##### ZPA/Azure Private DNS specific variables ##### +##################################################################################################################### +## 21. Provide the domain names you want Azure Private DNS to redirect to Cloud Connector for ZPA interception. +## Only applicable for base + zpa or zpa_enabled = true deployment types where Outbound DNS subnets, Resolver Ruleset/Rules, +## and Outbound Endpoints are being created. Two example domains are populated to show the mapping structure and syntax. +## Azure does require a trailing dot "." on all domain entries. ZPA Module will read through each to create a resolver rule per +## domain_names entry. Ucomment domain_names variable and add any additional appsegXX mappings as needed. + +#domain_names = { +# appseg1 = "app1.com." +# appseg2 = "app2.com." +#} + +## 22. Azure Private DNS queries will be conditionally forwarded to these target IP addresses. Default are a pair of Zscaler Global VIP addresses. +## The required expectation is that the target should follow VNet/subnet routing towards the configured Cloud Connector Load Balancer VIP for +## ZPA DNS interception + +#target_address = ["185.46.212.88", "185.46.212.89"] diff --git a/examples/base_1cc_zpa/variables.tf b/examples/base_1cc_zpa/variables.tf new file mode 100755 index 0000000..db3e38e --- /dev/null +++ b/examples/base_1cc_zpa/variables.tf @@ -0,0 +1,262 @@ +variable "env_subscription_id" { + type = string + description = "Azure Subscription ID where resources are to be deployed in" + sensitive = true +} + +variable "arm_location" { + type = string + description = "The Azure Region where resources are to be deployed" + default = "westus2" +} + +variable "name_prefix" { + type = string + description = "The name prefix for all your resources" + default = "zscc" + validation { + condition = length(var.name_prefix) <= 12 + error_message = "Variable name_prefix must be 12 or less characters." + } +} + +variable "network_address_space" { + type = string + description = "VNet IP CIDR Range. All subnet resources that might get created (public, workload, cloud connector) are derived from this /16 CIDR. If you require creating a VNet smaller than /16, you may need to explicitly define all other subnets via public_subnets, workload_subnets, cc_subnets, and route53_subnets variables" + default = "10.1.0.0/16" +} + +variable "cc_subnets" { + type = list(string) + description = "Cloud Connector Subnets to create in VNet. This is only required if you want to override the default subnets that this code creates via network_address_space variable." + default = null +} + +variable "workloads_subnets" { + type = list(string) + description = "Workload Subnets to create in VNet. This is only required if you want to override the default subnets that this code creates via network_address_space variable." + default = null +} + +variable "public_subnets" { + type = list(string) + description = "Public/Bastion Subnets to create in VNet. This is only required if you want to override the default subnets that this code creates via network_address_space variable." + default = null +} + +variable "private_dns_subnet" { + type = string + description = "Private DNS Resolver Outbound Endpoint Subnet to create in VNet. This is only required if you want to override the default subnet that this code creates via network_address_space variable." + default = null +} + +variable "environment" { + type = string + description = "Customer defined environment tag. ie: Dev, QA, Prod, etc." + default = "Development" +} + +variable "owner_tag" { + type = string + description = "Customer defined owner tag value. ie: Org, Dept, username, etc." + default = "zscc-admin" +} + +variable "tls_key_algorithm" { + type = string + description = "algorithm for tls_private_key resource" + default = "RSA" +} + +variable "managed_identity_subscription_id" { + type = string + description = "Azure Subscription ID where the User Managed Identity resource exists. Only required if this Subscription ID is different than env_subscription_id" + default = null + sensitive = true +} + +variable "cc_vm_managed_identity_name" { + type = string + description = "Azure Managed Identity name to attach to the CC VM. E.g zspreview-66117-mi" +} + +variable "cc_vm_managed_identity_rg" { + type = string + description = "Resource Group of the Azure Managed Identity name to attach to the CC VM. E.g. edgeconnector_rg_1" +} + +variable "cc_vm_prov_url" { + type = string + description = "Zscaler Cloud Connector Provisioning URL" +} + +variable "azure_vault_url" { + type = string + description = "Azure Vault URL" +} + +variable "ccvm_instance_type" { + type = string + description = "Cloud Connector Image size" + default = "Standard_D2s_v3" + validation { + condition = ( + var.ccvm_instance_type == "Standard_D2s_v3" || + var.ccvm_instance_type == "Standard_DS3_v2" || + var.ccvm_instance_type == "Standard_D8s_v3" || + var.ccvm_instance_type == "Standard_D16s_v3" || + var.ccvm_instance_type == "Standard_DS5_v2" + ) + error_message = "Input ccvm_instance_type must be set to an approved vm size." + } +} + +variable "cc_instance_size" { + type = string + description = "Cloud Connector Instance size. Determined by and needs to match the Cloud Connector Portal provisioning template configuration" + default = "small" + validation { + condition = ( + var.cc_instance_size == "small" || + var.cc_instance_size == "medium" || + var.cc_instance_size == "large" + ) + error_message = "Input cc_instance_size must be set to an approved cc instance type." + } +} + +# Validation to determine if the selected Azure VM type and CC VM size is compatible +locals { + small_cc_instance = ["Standard_D2s_v3", "Standard_DS3_v2", "Standard_D8s_v3", "Standard_D16s_v3", "Standard_DS5_v2"] + medium_cc_instance = ["Standard_DS3_v2", "Standard_D8s_v3", "Standard_D16s_v3", "Standard_DS5_v2"] + large_cc_instance = ["Standard_D16s_v3", "Standard_DS5_v2"] + + valid_cc_create = ( + contains(local.small_cc_instance, var.ccvm_instance_type) && var.cc_instance_size == "small" || + contains(local.medium_cc_instance, var.ccvm_instance_type) && var.cc_instance_size == "medium" || + contains(local.large_cc_instance, var.ccvm_instance_type) && var.cc_instance_size == "large" + ) +} + +variable "ccvm_image_publisher" { + type = string + description = "Azure Marketplace Cloud Connector Image Publisher" + default = "zscaler1579058425289" +} + +variable "ccvm_image_offer" { + type = string + description = "Azure Marketplace Cloud Connector Image Offer" + default = "zia_cloud_connector" +} + +variable "ccvm_image_sku" { + type = string + description = "Azure Marketplace Cloud Connector Image SKU" + default = "zs_ser_gen1_cc_01" +} + +variable "ccvm_image_version" { + type = string + description = "Azure Marketplace Cloud Connector Image Version" + default = "latest" +} + +variable "http_probe_port" { + type = number + description = "Port number for Cloud Connector cloud init to enable listener port for HTTP probe from Azure LB" + default = 50000 + validation { + condition = ( + tonumber(var.http_probe_port) == 80 || + (tonumber(var.http_probe_port) >= 1024 && tonumber(var.http_probe_port) <= 65535) + ) + error_message = "Input http_probe_port must be set to a single value of 80 or any number between 1024-65535." + } +} + +variable "workload_count" { + type = number + description = "The number of Workload VMs to deploy" + default = 1 + validation { + condition = var.workload_count >= 1 && var.workload_count <= 250 + error_message = "Input workload_count must be a whole number between 1 and 250." + } +} + +variable "cc_count" { + type = number + description = "The number of Cloud Connectors to deploy. Validation assumes max for /24 subnet but could be smaller or larger as long as subnet can accommodate" + default = 1 + validation { + condition = var.cc_count >= 1 && var.cc_count <= 250 + error_message = "Input cc_count must be a whole number between 1 and 250." + } +} + +variable "zones_enabled" { + type = bool + description = "Determine whether to provision Cloud Connector VMs explicitly in defined zones (if supported by the Azure region provided in the location variable). If left false, Azure will automatically choose a zone and module will create an availability set resource instead for VM fault tolerance" + default = false +} + +variable "zones" { + type = list(string) + description = "Specify which availability zone(s) to deploy VM resources in if zones_enabled variable is set to true" + default = ["1"] + validation { + condition = ( + !contains([for zones in var.zones : contains(["1", "2", "3"], zones)], false) + ) + error_message = "Input zones variable must be a number 1-3." + } +} + +variable "reuse_nsg" { + type = bool + description = "Specifies whether the NSG module should create 1:1 network security groups per instance or 1 network security group for all instances" + default = "false" +} + +variable "accelerated_networking_enabled" { + type = bool + description = "Enable/Disable accelerated networking support on all Cloud Connector service interfaces" + default = true +} + +variable "bastion_nsg_source_prefix" { + type = string + description = "user input for locking down SSH access to bastion to a specific IP or CIDR range" + default = "*" +} + +variable "lb_enabled" { + type = bool + description = "Default true. Only relevant for 'base' deployments. Configure Workload Route Table to default route next hop to the CC Load Balancer IP passed from var.lb_frontend_ip. If false, default route next hop directly to the CC Service IP passed from var.cc_service_ip" + default = false +} + +variable "encryption_at_host_enabled" { + type = bool + description = "User input for enabling or disabling host encryption" + default = true +} + +# Azure Private DNS specific variables +variable "zpa_enabled" { + type = bool + description = "Configure Azure Private DNS Outbound subnet, Resolvers, Rulesets/Rules, and Outbound Endpoint ZPA DNS redirection" + default = true +} + +variable "domain_names" { + type = map(any) + description = "Domain names fqdn/wildcard to have Azure Private DNS redirect DNS requests to Cloud Connector" +} + +variable "target_address" { + type = list(string) + description = "Azure DNS queries will be conditionally forwarded to these target IP addresses. Default are a pair of Zscaler Global VIP addresses" + default = ["185.46.212.88", "185.46.212.89"] +} diff --git a/examples/base_1cc_zpa/versions.tf b/examples/base_1cc_zpa/versions.tf new file mode 100755 index 0000000..82f3301 --- /dev/null +++ b/examples/base_1cc_zpa/versions.tf @@ -0,0 +1,36 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">= 3.46, <= 3.74" + } + random = { + source = "hashicorp/random" + version = "~> 3.3.0" + } + local = { + source = "hashicorp/local" + version = "~> 2.2.0" + } + null = { + source = "hashicorp/null" + version = "~> 3.1.0" + } + tls = { + source = "hashicorp/tls" + version = "~> 3.4.0" + } + } + required_version = ">= 0.13.7, < 2.0.0" +} + +provider "azurerm" { + features {} +} + +provider "azurerm" { + alias = "managed_identity_sub" + subscription_id = var.managed_identity_subscription_id == null ? var.env_subscription_id : var.managed_identity_subscription_id + features {} + skip_provider_registration = true +} diff --git a/examples/base_cc_lb/README.md b/examples/base_cc_lb/README.md index 2f831d2..75b2e93 100644 --- a/examples/base_cc_lb/README.md +++ b/examples/base_cc_lb/README.md @@ -42,7 +42,7 @@ From base_cc_lb directory execute: | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 0.13.7, < 2.0.0 | -| [azurerm](#requirement\_azurerm) | ~> 3.46.0 | +| [azurerm](#requirement\_azurerm) | >= 3.46, <= 3.74 | | [local](#requirement\_local) | ~> 2.2.0 | | [null](#requirement\_null) | ~> 3.1.0 | | [random](#requirement\_random) | ~> 3.3.0 | @@ -99,15 +99,19 @@ From base_cc_lb directory execute: | [ccvm\_image\_sku](#input\_ccvm\_image\_sku) | Azure Marketplace Cloud Connector Image SKU | `string` | `"zs_ser_gen1_cc_01"` | no | | [ccvm\_image\_version](#input\_ccvm\_image\_version) | Azure Marketplace Cloud Connector Image Version | `string` | `"latest"` | no | | [ccvm\_instance\_type](#input\_ccvm\_instance\_type) | Cloud Connector Image size | `string` | `"Standard_D2s_v3"` | no | +| [encryption\_at\_host\_enabled](#input\_encryption\_at\_host\_enabled) | User input for enabling or disabling host encryption | `bool` | `true` | no | | [env\_subscription\_id](#input\_env\_subscription\_id) | Azure Subscription ID where resources are to be deployed in | `string` | n/a | yes | | [environment](#input\_environment) | Customer defined environment tag. ie: Dev, QA, Prod, etc. | `string` | `"Development"` | no | +| [health\_check\_interval](#input\_health\_check\_interval) | The interval, in seconds, for how frequently to probe the endpoint for health status. Typically, the interval is slightly less than half the allocated timeout period (in seconds) which allows two full probes before taking the instance out of rotation. The default value is 15, the minimum value is 5 | `number` | `15` | no | | [http\_probe\_port](#input\_http\_probe\_port) | Port number for Cloud Connector cloud init to enable listener port for HTTP probe from Azure LB | `number` | `50000` | no | | [lb\_enabled](#input\_lb\_enabled) | Default true. Only relevant for 'base' deployments. Configure Workload Route Table to default route next hop to the CC Load Balancer IP passed from var.lb\_frontend\_ip. If false, default route next hop directly to the CC Service IP passed from var.cc\_service\_ip | `bool` | `true` | no | -| [load\_distribution](#input\_load\_distribution) | Azure LB load distribution method | `string` | `"SourceIP"` | no | +| [load\_distribution](#input\_load\_distribution) | Azure LB load distribution method | `string` | `"Default"` | no | | [managed\_identity\_subscription\_id](#input\_managed\_identity\_subscription\_id) | Azure Subscription ID where the User Managed Identity resource exists. Only required if this Subscription ID is different than env\_subscription\_id | `string` | `null` | no | -| [name\_prefix](#input\_name\_prefix) | The name prefix for all your resources | `string` | `"zsdemo"` | no | +| [name\_prefix](#input\_name\_prefix) | The name prefix for all your resources | `string` | `"zscc"` | no | | [network\_address\_space](#input\_network\_address\_space) | VNet IP CIDR Range. All subnet resources that might get created (public, workload, cloud connector) are derived from this /16 CIDR. If you require creating a VNet smaller than /16, you may need to explicitly define all other subnets via public\_subnets, workload\_subnets, cc\_subnets, and route53\_subnets variables | `string` | `"10.1.0.0/16"` | no | +| [number\_of\_probes](#input\_number\_of\_probes) | The number of probes where if no response, will result in stopping further traffic from being delivered to the endpoint. This values allows endpoints to be taken out of rotation faster or slower than the typical times used in Azure | `number` | `1` | no | | [owner\_tag](#input\_owner\_tag) | Customer defined owner tag value. ie: Org, Dept, username, etc. | `string` | `"zscc-admin"` | no | +| [probe\_threshold](#input\_probe\_threshold) | The number of consecutive successful or failed probes in order to allow or deny traffic from being delivered to this endpoint. After failing the number of consecutive probes equal to this value, the endpoint will be taken out of rotation and require the same number of successful consecutive probes to be placed back in rotation. | `number` | `2` | no | | [public\_subnets](#input\_public\_subnets) | Public/Bastion Subnets to create in VNet. This is only required if you want to override the default subnets that this code creates via network\_address\_space variable. | `list(string)` | `null` | no | | [reuse\_nsg](#input\_reuse\_nsg) | Specifies whether the NSG module should create 1:1 network security groups per instance or 1 network security group for all instances | `bool` | `"false"` | no | | [tls\_key\_algorithm](#input\_tls\_key\_algorithm) | algorithm for tls\_private\_key resource | `string` | `"RSA"` | no | diff --git a/examples/base_cc_lb/main.tf b/examples/base_cc_lb/main.tf index 4fd5159..bd3dbad 100755 --- a/examples/base_cc_lb/main.tf +++ b/examples/base_cc_lb/main.tf @@ -93,7 +93,7 @@ module "workload" { resource_group = module.network.resource_group_name subnet_id = module.network.workload_subnet_ids[0] ssh_key = tls_private_key.key.public_key_openssh - dns_servers = ["185.46.212.88", "185.46.212.89"] + dns_servers = [] } @@ -111,6 +111,7 @@ locals { CC_URL=${var.cc_vm_prov_url} AZURE_VAULT_URL=${var.azure_vault_url} HTTP_PROBE_PORT=${var.http_probe_port} +AZURE_MANAGED_IDENTITY_CLIENT_ID=${module.cc_identity.managed_identity_client_id} USERDATA } @@ -147,6 +148,7 @@ module "cc_vm" { mgmt_nsg_id = module.cc_nsg.mgmt_nsg_id service_nsg_id = module.cc_nsg.service_nsg_id accelerated_networking_enabled = var.accelerated_networking_enabled + encryption_at_host_enabled = var.encryption_at_host_enabled depends_on = [ local_file.user_data_file, @@ -194,17 +196,20 @@ module "cc_identity" { ################################################################################ # Azure Load Balancer Module variables module "cc_lb" { - source = "../../modules/terraform-zscc-lb-azure" - name_prefix = var.name_prefix - resource_tag = random_string.suffix.result - global_tags = local.global_tags - resource_group = module.network.resource_group_name - location = var.arm_location - subnet_id = module.network.cc_subnet_ids[0] - http_probe_port = var.http_probe_port - load_distribution = var.load_distribution - zones_enabled = var.zones_enabled - zones = var.zones + source = "../../modules/terraform-zscc-lb-azure" + name_prefix = var.name_prefix + resource_tag = random_string.suffix.result + global_tags = local.global_tags + resource_group = module.network.resource_group_name + location = var.arm_location + subnet_id = module.network.cc_subnet_ids[0] + http_probe_port = var.http_probe_port + load_distribution = var.load_distribution + zones_enabled = var.zones_enabled + zones = var.zones + health_check_interval = var.health_check_interval + probe_threshold = var.probe_threshold + number_of_probes = var.number_of_probes } diff --git a/examples/base_cc_lb/outputs.tf b/examples/base_cc_lb/outputs.tf index d0773a4..cf8170d 100755 --- a/examples/base_cc_lb/outputs.tf +++ b/examples/base_cc_lb/outputs.tf @@ -1,6 +1,20 @@ locals { testbedconfig = < zones. ## Not configurable for base or base_1cc deployment types. (All others - Default: 2) ## E.g. cc_count set to 4 and 2 zones set ['1","2"] will create 2x CCs in AZ1 and 2x CCs in AZ2 #cc_count = 2 - -## 11. By default, no zones are specified in any resource creation meaning they are either auto-assigned by Azure +## 13. By default, no zones are specified in any resource creation meaning they are either auto-assigned by Azure ## (Virtual Machines and NAT Gateways) or Zone-Redundant (Public IP) based on whatever default configuration is. ## Setting this value to true will do the following: ## 1. will create zonal NAT Gateway resources in order of the zones [1-3] specified in zones variable. 1x per zone @@ -102,8 +103,7 @@ #zones_enabled = true - -## 12. By default, this variable is used as a count (1) for resource creation of Public IP, NAT Gateway, and CC Subnets. +## 14. By default, this variable is used as a count (1) for resource creation of Public IP, NAT Gateway, and CC Subnets. ## This should only be modified if zones_enabled is also set to true ## Doing so will change the default zone aware configuration for the 3 aforementioned resources with the values specified ## @@ -118,8 +118,7 @@ #zones = ["1","2"] #zones = ["1","2","3"] - -## 13. Network Configuration: +## 15. Network Configuration: ## IPv4 CIDR configured with VNet creation. All Subnet resources (Workload, Public, and Cloud Connector) will be created based off this prefix ## /24 subnets are created assuming this cidr is a /16. If you require creating a VNet smaller than /16, you may need to explicitly define all other @@ -143,24 +142,29 @@ #workloads_subnets = ["10.x.y.z/24","10.x.y.z/24"] #cc_subnets = ["10.x.y.z/24","10.x.y.z/24"] - -## 14. Number of Workload VMs to be provisioned in the workload subnet. Only limitation is available IP space +## 16. Number of Workload VMs to be provisioned in the workload subnet. Only limitation is available IP space ## in subnet configuration. Only applicable for "base" deployment types. Default workload subnet is /24 so 250 max #workload_count = 2 - -## 15. Tag attribute "Owner" assigned to all resoure creation. (Default: "zscc-admin") +## 17. Tag attribute "Owner" assigned to all resoure creation. (Default: "zscc-admin") #owner_tag = "username@company.com" - -## 16. Tag attribute "Environment" assigned to all resources created. (Default: "Development") +## 18. Tag attribute "Environment" assigned to all resources created. (Default: "Development") #environment = "Development" - -## 17. By default, this script will apply 1 Network Security Group per Cloud Connector instance. +## 19. By default, this script will apply 1 Network Security Group per Cloud Connector instance. ## Uncomment if you want to use the same Network Security Group for ALL Cloud Connectors (true or false. Default: false) #reuse_nsg = true + +## 20. By default, Host encryption is enabled for Cloud Connector VMs. This does require the EncryptionAtHost feature +## enabled for your subscription though first. +## You can verify this by following the Azure Prerequisites guide here: +## https://learn.microsoft.com/en-us/azure/virtual-machines/linux/disks-enable-host-based-encryption-cli#prerequisites +## +## Uncomment if you want to not enable this VM setting + +#encryption_at_host_enabled = false diff --git a/examples/base_cc_lb/variables.tf b/examples/base_cc_lb/variables.tf index 59b1147..e4b762b 100755 --- a/examples/base_cc_lb/variables.tf +++ b/examples/base_cc_lb/variables.tf @@ -13,7 +13,11 @@ variable "arm_location" { variable "name_prefix" { type = string description = "The name prefix for all your resources" - default = "zsdemo" + default = "zscc" + validation { + condition = length(var.name_prefix) <= 12 + error_message = "Variable name_prefix must be 12 or less characters." + } } variable "network_address_space" { @@ -224,7 +228,7 @@ variable "bastion_nsg_source_prefix" { variable "load_distribution" { type = string description = "Azure LB load distribution method" - default = "SourceIP" + default = "Default" validation { condition = ( var.load_distribution == "SourceIP" || @@ -240,3 +244,33 @@ variable "lb_enabled" { description = "Default true. Only relevant for 'base' deployments. Configure Workload Route Table to default route next hop to the CC Load Balancer IP passed from var.lb_frontend_ip. If false, default route next hop directly to the CC Service IP passed from var.cc_service_ip" default = true } + +variable "health_check_interval" { + type = number + description = "The interval, in seconds, for how frequently to probe the endpoint for health status. Typically, the interval is slightly less than half the allocated timeout period (in seconds) which allows two full probes before taking the instance out of rotation. The default value is 15, the minimum value is 5" + default = 15 + validation { + condition = ( + var.health_check_interval > 4 + ) + error_message = "Input health_check_interval must be a number 5 or greater." + } +} + +variable "probe_threshold" { + type = number + description = "The number of consecutive successful or failed probes in order to allow or deny traffic from being delivered to this endpoint. After failing the number of consecutive probes equal to this value, the endpoint will be taken out of rotation and require the same number of successful consecutive probes to be placed back in rotation." + default = 2 +} + +variable "number_of_probes" { + type = number + description = "The number of probes where if no response, will result in stopping further traffic from being delivered to the endpoint. This values allows endpoints to be taken out of rotation faster or slower than the typical times used in Azure" + default = 1 +} + +variable "encryption_at_host_enabled" { + type = bool + description = "User input for enabling or disabling host encryption" + default = true +} diff --git a/examples/base_cc_lb/versions.tf b/examples/base_cc_lb/versions.tf index e97eecf..82f3301 100755 --- a/examples/base_cc_lb/versions.tf +++ b/examples/base_cc_lb/versions.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "~> 3.46.0" + version = ">= 3.46, <= 3.74" } random = { source = "hashicorp/random" diff --git a/examples/base_cc_lb_zpa/README.md b/examples/base_cc_lb_zpa/README.md new file mode 100644 index 0000000..7d4adc8 --- /dev/null +++ b/examples/base_cc_lb_zpa/README.md @@ -0,0 +1,135 @@ +# Zscaler "Base_cc_lb_zpa" deployment type + +This deployment type is intended for greenfield/pov/lab purposes. It will deploy a fully functioning sandbox environment in a new Resource Group/VNet with test workload VMs. Full set of resources provisioned listed below, but this will effectively create all network infrastructure dependencies for an Azure environment. Everything from "Base" deployment type (Creates 1 new Resource Group; 1 VNet with 1 public subnet and 1 private/workload subnet; 1 Centos server workload in the private subnet; 1 Bastion Host in the public subnet assigned a Public IP; and generates local key pair .pem file for ssh access).
+ +Additionally: Creates 2 Cloud Connector private subnets associated to a 2 NAT Gateways; 2 Cloud Connector VMs; Standard Azure Load Balancer; and workload private subnet UDR routing to the Load Balancer Frontend IP. + +## Caveats/Considerations +- WSL2 DNS bug: If you are trying to run these Azure terraform deployments specifically from a Windows WSL2 instance like Ubuntu and receive an error containing a message similar to this "dial tcp: lookup management.azure.com on 172.21.240.1:53: cannot unmarshal DNS message" please refer here for a WSL2 resolv.conf fix. https://github.com/microsoft/WSL/issues/5420#issuecomment-646479747. + +## How to deploy: + +### Option 1 (guided): +From the examples directory, run the zsec bash script that walks to all required inputs. +- ./zsec up +- enter "greenfield" +- enter "base_cc_lb_zpa" +- follow the remainder of the authentication and configuration input prompts. +- script will detect client operating system and download/run a specific version of terraform in a temporary bin directory +- inputs will be validated and terraform init/apply will automatically exectute. +- verify all resources that will be created/modified and enter "yes" to confirm + +### Option 2 (manual): +Modify/populate any required variable input values in base_cc_lb_zpa/terraform.tfvars file and save. + +From base_cc_lb_zpa directory execute: +- terraform init +- terraform apply + +## How to destroy: + +### Option 1 (guided): +From the examples directory, run the zsec bash script that walks to all required inputs. +- ./zsec destroy + +### Option 2 (manual): +From base_cc_lb_zpa directory execute: +- terraform destroy + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 0.13.7, < 2.0.0 | +| [azurerm](#requirement\_azurerm) | >= 3.46, <= 3.74 | +| [local](#requirement\_local) | ~> 2.2.0 | +| [null](#requirement\_null) | ~> 3.1.0 | +| [random](#requirement\_random) | ~> 3.3.0 | +| [tls](#requirement\_tls) | ~> 3.4.0 | + +## Providers + +| Name | Version | +|------|---------| +| [azurerm](#provider\_azurerm) | >= 3.46, <= 3.74 | +| [local](#provider\_local) | ~> 2.2.0 | +| [null](#provider\_null) | ~> 3.1.0 | +| [random](#provider\_random) | ~> 3.3.0 | +| [tls](#provider\_tls) | ~> 3.4.0 | + +## Modules + +| Name | Source | Version | +|------|--------|---------| +| [bastion](#module\_bastion) | ../../modules/terraform-zscc-bastion-azure | n/a | +| [cc\_identity](#module\_cc\_identity) | ../../modules/terraform-zscc-identity-azure | n/a | +| [cc\_lb](#module\_cc\_lb) | ../../modules/terraform-zscc-lb-azure | n/a | +| [cc\_nsg](#module\_cc\_nsg) | ../../modules/terraform-zscc-nsg-azure | n/a | +| [cc\_vm](#module\_cc\_vm) | ../../modules/terraform-zscc-ccvm-azure | n/a | +| [network](#module\_network) | ../../modules/terraform-zscc-network-azure | n/a | +| [private\_dns](#module\_private\_dns) | ../../modules/terraform-zscc-private-dns-azure | n/a | +| [workload](#module\_workload) | ../../modules/terraform-zscc-workload-azure | n/a | + +## Resources + +| Name | Type | +|------|------| +| [azurerm_private_dns_resolver_virtual_network_link.dns_vnet_link](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_resolver_virtual_network_link) | resource | +| [local_file.private_key](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [local_file.testbed](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [local_file.user_data_file](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | +| [null_resource.cc_error_checker](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | +| [random_string.suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource | +| [tls_private_key.key](https://registry.terraform.io/providers/hashicorp/tls/latest/docs/resources/private_key) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [accelerated\_networking\_enabled](#input\_accelerated\_networking\_enabled) | Enable/Disable accelerated networking support on all Cloud Connector service interfaces | `bool` | `true` | no | +| [arm\_location](#input\_arm\_location) | The Azure Region where resources are to be deployed | `string` | `"westus2"` | no | +| [azure\_vault\_url](#input\_azure\_vault\_url) | Azure Vault URL | `string` | n/a | yes | +| [bastion\_nsg\_source\_prefix](#input\_bastion\_nsg\_source\_prefix) | user input for locking down SSH access to bastion to a specific IP or CIDR range | `string` | `"*"` | no | +| [cc\_count](#input\_cc\_count) | The number of Cloud Connectors to deploy. Validation assumes max for /24 subnet but could be smaller or larger as long as subnet can accommodate | `number` | `2` | no | +| [cc\_instance\_size](#input\_cc\_instance\_size) | Cloud Connector Instance size. Determined by and needs to match the Cloud Connector Portal provisioning template configuration | `string` | `"small"` | no | +| [cc\_subnets](#input\_cc\_subnets) | Cloud Connector Subnets to create in VNet. This is only required if you want to override the default subnets that this code creates via network\_address\_space variable. | `list(string)` | `null` | no | +| [cc\_vm\_managed\_identity\_name](#input\_cc\_vm\_managed\_identity\_name) | Azure Managed Identity name to attach to the CC VM. E.g zspreview-66117-mi | `string` | n/a | yes | +| [cc\_vm\_managed\_identity\_rg](#input\_cc\_vm\_managed\_identity\_rg) | Resource Group of the Azure Managed Identity name to attach to the CC VM. E.g. edgeconnector\_rg\_1 | `string` | n/a | yes | +| [cc\_vm\_prov\_url](#input\_cc\_vm\_prov\_url) | Zscaler Cloud Connector Provisioning URL | `string` | n/a | yes | +| [ccvm\_image\_offer](#input\_ccvm\_image\_offer) | Azure Marketplace Cloud Connector Image Offer | `string` | `"zia_cloud_connector"` | no | +| [ccvm\_image\_publisher](#input\_ccvm\_image\_publisher) | Azure Marketplace Cloud Connector Image Publisher | `string` | `"zscaler1579058425289"` | no | +| [ccvm\_image\_sku](#input\_ccvm\_image\_sku) | Azure Marketplace Cloud Connector Image SKU | `string` | `"zs_ser_gen1_cc_01"` | no | +| [ccvm\_image\_version](#input\_ccvm\_image\_version) | Azure Marketplace Cloud Connector Image Version | `string` | `"latest"` | no | +| [ccvm\_instance\_type](#input\_ccvm\_instance\_type) | Cloud Connector Image size | `string` | `"Standard_D2s_v3"` | no | +| [domain\_names](#input\_domain\_names) | Domain names fqdn/wildcard to have Azure Private DNS redirect DNS requests to Cloud Connector | `map(any)` | n/a | yes | +| [encryption\_at\_host\_enabled](#input\_encryption\_at\_host\_enabled) | User input for enabling or disabling host encryption | `bool` | `true` | no | +| [env\_subscription\_id](#input\_env\_subscription\_id) | Azure Subscription ID where resources are to be deployed in | `string` | n/a | yes | +| [environment](#input\_environment) | Customer defined environment tag. ie: Dev, QA, Prod, etc. | `string` | `"Development"` | no | +| [health\_check\_interval](#input\_health\_check\_interval) | The interval, in seconds, for how frequently to probe the endpoint for health status. Typically, the interval is slightly less than half the allocated timeout period (in seconds) which allows two full probes before taking the instance out of rotation. The default value is 15, the minimum value is 5 | `number` | `15` | no | +| [http\_probe\_port](#input\_http\_probe\_port) | Port number for Cloud Connector cloud init to enable listener port for HTTP probe from Azure LB | `number` | `50000` | no | +| [lb\_enabled](#input\_lb\_enabled) | Default true. Only relevant for 'base' deployments. Configure Workload Route Table to default route next hop to the CC Load Balancer IP passed from var.lb\_frontend\_ip. If false, default route next hop directly to the CC Service IP passed from var.cc\_service\_ip | `bool` | `true` | no | +| [load\_distribution](#input\_load\_distribution) | Azure LB load distribution method | `string` | `"Default"` | no | +| [managed\_identity\_subscription\_id](#input\_managed\_identity\_subscription\_id) | Azure Subscription ID where the User Managed Identity resource exists. Only required if this Subscription ID is different than env\_subscription\_id | `string` | `null` | no | +| [name\_prefix](#input\_name\_prefix) | The name prefix for all your resources | `string` | `"zscc"` | no | +| [network\_address\_space](#input\_network\_address\_space) | VNet IP CIDR Range. All subnet resources that might get created (public, workload, cloud connector) are derived from this /16 CIDR. If you require creating a VNet smaller than /16, you may need to explicitly define all other subnets via public\_subnets, workload\_subnets, cc\_subnets, and route53\_subnets variables | `string` | `"10.1.0.0/16"` | no | +| [number\_of\_probes](#input\_number\_of\_probes) | The number of probes where if no response, will result in stopping further traffic from being delivered to the endpoint. This values allows endpoints to be taken out of rotation faster or slower than the typical times used in Azure | `number` | `1` | no | +| [owner\_tag](#input\_owner\_tag) | Customer defined owner tag value. ie: Org, Dept, username, etc. | `string` | `"zscc-admin"` | no | +| [private\_dns\_subnet](#input\_private\_dns\_subnet) | Private DNS Resolver Outbound Endpoint Subnet to create in VNet. This is only required if you want to override the default subnet that this code creates via network\_address\_space variable. | `string` | `null` | no | +| [probe\_threshold](#input\_probe\_threshold) | The number of consecutive successful or failed probes in order to allow or deny traffic from being delivered to this endpoint. After failing the number of consecutive probes equal to this value, the endpoint will be taken out of rotation and require the same number of successful consecutive probes to be placed back in rotation. | `number` | `2` | no | +| [public\_subnets](#input\_public\_subnets) | Public/Bastion Subnets to create in VNet. This is only required if you want to override the default subnets that this code creates via network\_address\_space variable. | `list(string)` | `null` | no | +| [reuse\_nsg](#input\_reuse\_nsg) | Specifies whether the NSG module should create 1:1 network security groups per instance or 1 network security group for all instances | `bool` | `"false"` | no | +| [target\_address](#input\_target\_address) | Azure DNS queries will be conditionally forwarded to these target IP addresses. Default are a pair of Zscaler Global VIP addresses | `list(string)` |
[
"185.46.212.88",
"185.46.212.89"
]
| no | +| [tls\_key\_algorithm](#input\_tls\_key\_algorithm) | algorithm for tls\_private\_key resource | `string` | `"RSA"` | no | +| [workload\_count](#input\_workload\_count) | The number of Workload VMs to deploy | `number` | `1` | no | +| [workloads\_subnets](#input\_workloads\_subnets) | Workload Subnets to create in VNet. This is only required if you want to override the default subnets that this code creates via network\_address\_space variable. | `list(string)` | `null` | no | +| [zones](#input\_zones) | Specify which availability zone(s) to deploy VM resources in if zones\_enabled variable is set to true | `list(string)` |
[
"1"
]
| no | +| [zones\_enabled](#input\_zones\_enabled) | Determine whether to provision Cloud Connector VMs explicitly in defined zones (if supported by the Azure region provided in the location variable). If left false, Azure will automatically choose a zone and module will create an availability set resource instead for VM fault tolerance | `bool` | `false` | no | +| [zpa\_enabled](#input\_zpa\_enabled) | Configure Azure Private DNS Outbound subnet, Resolvers, Rulesets/Rules, and Outbound Endpoint ZPA DNS redirection | `bool` | `true` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| [testbedconfig](#output\_testbedconfig) | Azure Testbed results | + diff --git a/examples/base_cc_lb_zpa/backend.tf b/examples/base_cc_lb_zpa/backend.tf new file mode 100755 index 0000000..b739715 --- /dev/null +++ b/examples/base_cc_lb_zpa/backend.tf @@ -0,0 +1,5 @@ +terraform { + backend "local" { + path = "../terraform.tfstate" + } +} diff --git a/examples/base_cc_lb_zpa/main.tf b/examples/base_cc_lb_zpa/main.tf new file mode 100755 index 0000000..0789327 --- /dev/null +++ b/examples/base_cc_lb_zpa/main.tf @@ -0,0 +1,264 @@ +################################################################################ +# Generate a unique random string for resource name assignment and key pair +################################################################################ +resource "random_string" "suffix" { + length = 8 + upper = false + special = false +} + + +################################################################################ +# Map default tags with values to be assigned to all tagged resources +################################################################################ +locals { + global_tags = { + Owner = var.owner_tag + ManagedBy = "terraform" + Vendor = "Zscaler" + Environment = var.environment + } +} + + +################################################################################ +# The following lines generates a new SSH key pair and stores the PEM file +# locally. The public key output is used as the instance_key passed variable +# to the vm modules for admin_ssh_key public_key authentication. +# This is not recommended for production deployments. Please consider modifying +# to pass your own custom public key file located in a secure location. +################################################################################ +# private key for login +resource "tls_private_key" "key" { + algorithm = var.tls_key_algorithm +} + +# write private key to local pem file +resource "local_file" "private_key" { + content = tls_private_key.key.private_key_pem + filename = "../${var.name_prefix}-key-${random_string.suffix.result}.pem" + file_permission = "0600" +} + + +################################################################################ +# 1. Create/reference all network infrastructure resource dependencies for all +# child modules (Resource Group, VNet, Subnets, NAT Gateway, Route Tables) +################################################################################ +module "network" { + source = "../../modules/terraform-zscc-network-azure" + name_prefix = var.name_prefix + resource_tag = random_string.suffix.result + global_tags = local.global_tags + location = var.arm_location + network_address_space = var.network_address_space + cc_subnets = var.cc_subnets + workloads_subnets = var.workloads_subnets + public_subnets = var.public_subnets + private_dns_subnet = var.private_dns_subnet + zones_enabled = var.zones_enabled + zones = var.zones + lb_frontend_ip = module.cc_lb.lb_ip + workloads_enabled = true + bastion_enabled = true + lb_enabled = var.lb_enabled + zpa_enabled = var.zpa_enabled +} + + +################################################################################ +# 2. Create Bastion Host for workload and CC SSH jump access +################################################################################ +module "bastion" { + source = "../../modules/terraform-zscc-bastion-azure" + location = var.arm_location + name_prefix = var.name_prefix + resource_tag = random_string.suffix.result + global_tags = local.global_tags + resource_group = module.network.resource_group_name + public_subnet_id = module.network.bastion_subnet_ids[0] + ssh_key = tls_private_key.key.public_key_openssh + bastion_nsg_source_prefix = var.bastion_nsg_source_prefix +} + + +################################################################################ +# 3. Create Workload Hosts to test traffic connectivity through CC +################################################################################ +module "workload" { + source = "../../modules/terraform-zscc-workload-azure" + workload_count = var.workload_count + location = var.arm_location + name_prefix = var.name_prefix + resource_tag = random_string.suffix.result + global_tags = local.global_tags + resource_group = module.network.resource_group_name + subnet_id = module.network.workload_subnet_ids[0] + ssh_key = tls_private_key.key.public_key_openssh + dns_servers = [] +} + + +################################################################################ +# 4. Create specified number of CC VMs per cc_count by default in an +# availability set for Azure Data Center fault tolerance. Optionally, deployed +# CCs can automatically span equally across designated availabilty zones +# if enabled via "zones_enabled" and "zones" variables. E.g. cc_count set to +# 4 and 2 zones ['1","2"] will create 2x CCs in AZ1 and 2x CCs in AZ2 +################################################################################ +# Create the user_data file with necessary bootstrap variables for Cloud Connector registration +locals { + userdata = <> ../errorlog.txt +EOF + } +} diff --git a/examples/base_cc_lb_zpa/outputs.tf b/examples/base_cc_lb_zpa/outputs.tf new file mode 100755 index 0000000..cfbeeec --- /dev/null +++ b/examples/base_cc_lb_zpa/outputs.tf @@ -0,0 +1,74 @@ +locals { + + testbedconfig = < zones. +## Not configurable for base or base_1cc deployment types. (All others - Default: 2) +## E.g. cc_count set to 4 and 2 zones set ['1","2"] will create 2x CCs in AZ1 and 2x CCs in AZ2 + +#cc_count = 2 + +## 13. By default, no zones are specified in any resource creation meaning they are either auto-assigned by Azure +## (Virtual Machines and NAT Gateways) or Zone-Redundant (Public IP) based on whatever default configuration is. +## Setting this value to true will do the following: +## 1. will create zonal NAT Gateway resources in order of the zones [1-3] specified in zones variable. 1x per zone +## 2. will NOT create availability set resource nor associate Cloud Connector VMs to one +## 3. will create zonal Cloud Connector Virtual Machine appliances looping through and alternating per the order of the zones +## [1-3] specified in the zones variable AND total number of Cloud Connectors specified in cc_count variable. +## (Default: false) + +#zones_enabled = true + +## 14. By default, this variable is used as a count (1) for resource creation of Public IP, NAT Gateway, and CC Subnets. +## This should only be modified if zones_enabled is also set to true +## Doing so will change the default zone aware configuration for the 3 aforementioned resources with the values specified +## +## Use case: Define zone numbers "1" and "2". This will create 2x Public IPs (one in zone 1; the other in zone 2), +## 2x NAT Gateways (one in zone 1; the other in zone 2), associate the zone 1 PIP w/ zone 1 NAT GW and the zone 2 +## PIP w/ zone 2 NAT GW, create 2x CC Subnets and associate subnet 1 w/ zone 1 NAT GW and subnet 2 w/ zone 2 NAT GW, +## then each CC created will be assigned a zone in the subnet corresponding to the same zone of the NAT GW and PIP associated. + +## Uncomment one of the desired zones configuration below. + +#zones = ["1"] +#zones = ["1","2"] +#zones = ["1","2","3"] + +## 15. Network Configuration: + +## IPv4 CIDR configured with VNet creation. All Subnet resources (Workload, Public, and Cloud Connector) will be created based off this prefix +## /24 subnets are created assuming this cidr is a /16. If you require creating a VNet smaller than /16, you may need to explicitly define all other +## subnets via public_subnets, workload_subnets, and cc_subnets variables (Default: "10.1.0.0/16") + +## Note: This variable only applies if you let Terraform create a new VNet. Custom deployment with byo_vnet enabled will ignore this + +#network_address_space = "10.1.0.0/16" + +## Subnet space. (Minimum /28 required. Default is null). If you do not specify subnets, they will automatically be assigned based on the default cidrsubnet +## creation within the VNet address_prefix block. Uncomment and modify if byo_vnet is set to true but byo_subnets is left false meaning you want terraform to create +## NEW subnets in that existing VNet. OR if you choose to modify the network_address_space from the default /16 so a smaller CIDR, you may need to edit the below variables +## to accommodate that address space. + +## ***** Note ***** +## It does not matter how many subnets you specify here. this script will only create in order 1 or as many as defined in the zones variable +## Default/Minumum: 1 - Maximum: 3 +## Example: If you change network_address_space to "10.2.0.0/24", set below variables to cidrs that fit in that /24 like cc_subnets = ["10.2.0.0/27","10.2.0.32/27"] etc. + +#public_subnets = ["10.x.y.z/24","10.x.y.z/24"] +#workloads_subnets = ["10.x.y.z/24","10.x.y.z/24"] +#cc_subnets = ["10.x.y.z/24","10.x.y.z/24"] +#private_dns_subnet = "10.x.y.z/28" + +## 16. Number of Workload VMs to be provisioned in the workload subnet. Only limitation is available IP space +## in subnet configuration. Only applicable for "base" deployment types. Default workload subnet is /24 so 250 max + +#workload_count = 2 + +## 17. Tag attribute "Owner" assigned to all resoure creation. (Default: "zscc-admin") + +#owner_tag = "username@company.com" + +## 18. Tag attribute "Environment" assigned to all resources created. (Default: "Development") + +#environment = "Development" + +## 19. By default, this script will apply 1 Network Security Group per Cloud Connector instance. +## Uncomment if you want to use the same Network Security Group for ALL Cloud Connectors (true or false. Default: false) + +#reuse_nsg = true + +## 20. By default, Host encryption is enabled for Cloud Connector VMs. This does require the EncryptionAtHost feature +## enabled for your subscription though first. +## You can verify this by following the Azure Prerequisites guide here: +## https://learn.microsoft.com/en-us/azure/virtual-machines/linux/disks-enable-host-based-encryption-cli#prerequisites +## +## Uncomment if you want to not enable this VM setting + +#encryption_at_host_enabled = false + + +##################################################################################################################### +##### ZPA/Azure Private DNS specific variables ##### +##################################################################################################################### +## 21. Provide the domain names you want Azure Private DNS to redirect to Cloud Connector for ZPA interception. +## Only applicable for base + zpa or zpa_enabled = true deployment types where Outbound DNS subnets, Resolver Ruleset/Rules, +## and Outbound Endpoints are being created. Two example domains are populated to show the mapping structure and syntax. +## Azure does require a trailing dot "." on all domain entries. ZPA Module will read through each to create a resolver rule per +## domain_names entry. Ucomment domain_names variable and add any additional appsegXX mappings as needed. + +#domain_names = { +# appseg1 = "app1.com." +# appseg2 = "app2.com." +#} + +## 22. Azure Private DNS queries will be conditionally forwarded to these target IP addresses. Default are a pair of Zscaler Global VIP addresses. +## The required expectation is that the target should follow VNet/subnet routing towards the configured Cloud Connector Load Balancer VIP for +## ZPA DNS interception + +#target_address = ["185.46.212.88", "185.46.212.89"] diff --git a/examples/base_cc_lb_zpa/variables.tf b/examples/base_cc_lb_zpa/variables.tf new file mode 100755 index 0000000..2df8851 --- /dev/null +++ b/examples/base_cc_lb_zpa/variables.tf @@ -0,0 +1,301 @@ +variable "env_subscription_id" { + type = string + description = "Azure Subscription ID where resources are to be deployed in" + sensitive = true +} + +variable "arm_location" { + type = string + description = "The Azure Region where resources are to be deployed" + default = "westus2" +} + +variable "name_prefix" { + type = string + description = "The name prefix for all your resources" + default = "zscc" + validation { + condition = length(var.name_prefix) <= 12 + error_message = "Variable name_prefix must be 12 or less characters." + } +} + +variable "network_address_space" { + type = string + description = "VNet IP CIDR Range. All subnet resources that might get created (public, workload, cloud connector) are derived from this /16 CIDR. If you require creating a VNet smaller than /16, you may need to explicitly define all other subnets via public_subnets, workload_subnets, cc_subnets, and route53_subnets variables" + default = "10.1.0.0/16" +} + +variable "cc_subnets" { + type = list(string) + description = "Cloud Connector Subnets to create in VNet. This is only required if you want to override the default subnets that this code creates via network_address_space variable." + default = null +} + +variable "workloads_subnets" { + type = list(string) + description = "Workload Subnets to create in VNet. This is only required if you want to override the default subnets that this code creates via network_address_space variable." + default = null +} + +variable "public_subnets" { + type = list(string) + description = "Public/Bastion Subnets to create in VNet. This is only required if you want to override the default subnets that this code creates via network_address_space variable." + default = null +} + +variable "private_dns_subnet" { + type = string + description = "Private DNS Resolver Outbound Endpoint Subnet to create in VNet. This is only required if you want to override the default subnet that this code creates via network_address_space variable." + default = null +} + +variable "environment" { + type = string + description = "Customer defined environment tag. ie: Dev, QA, Prod, etc." + default = "Development" +} + +variable "owner_tag" { + type = string + description = "Customer defined owner tag value. ie: Org, Dept, username, etc." + default = "zscc-admin" +} + +variable "tls_key_algorithm" { + type = string + description = "algorithm for tls_private_key resource" + default = "RSA" +} + +variable "managed_identity_subscription_id" { + type = string + description = "Azure Subscription ID where the User Managed Identity resource exists. Only required if this Subscription ID is different than env_subscription_id" + default = null + sensitive = true +} + +variable "cc_vm_managed_identity_name" { + type = string + description = "Azure Managed Identity name to attach to the CC VM. E.g zspreview-66117-mi" +} + +variable "cc_vm_managed_identity_rg" { + type = string + description = "Resource Group of the Azure Managed Identity name to attach to the CC VM. E.g. edgeconnector_rg_1" +} + +variable "cc_vm_prov_url" { + type = string + description = "Zscaler Cloud Connector Provisioning URL" +} + +variable "azure_vault_url" { + type = string + description = "Azure Vault URL" +} + +variable "ccvm_instance_type" { + type = string + description = "Cloud Connector Image size" + default = "Standard_D2s_v3" + validation { + condition = ( + var.ccvm_instance_type == "Standard_D2s_v3" || + var.ccvm_instance_type == "Standard_DS3_v2" || + var.ccvm_instance_type == "Standard_D8s_v3" || + var.ccvm_instance_type == "Standard_D16s_v3" || + var.ccvm_instance_type == "Standard_DS5_v2" + ) + error_message = "Input ccvm_instance_type must be set to an approved vm size." + } +} + +variable "cc_instance_size" { + type = string + description = "Cloud Connector Instance size. Determined by and needs to match the Cloud Connector Portal provisioning template configuration" + default = "small" + validation { + condition = ( + var.cc_instance_size == "small" || + var.cc_instance_size == "medium" || + var.cc_instance_size == "large" + ) + error_message = "Input cc_instance_size must be set to an approved cc instance type." + } +} + +# Validation to determine if the selected Azure VM type and CC VM size is compatible +locals { + small_cc_instance = ["Standard_D2s_v3", "Standard_DS3_v2", "Standard_D8s_v3", "Standard_D16s_v3", "Standard_DS5_v2"] + medium_cc_instance = ["Standard_DS3_v2", "Standard_D8s_v3", "Standard_D16s_v3", "Standard_DS5_v2"] + large_cc_instance = ["Standard_D16s_v3", "Standard_DS5_v2"] + + valid_cc_create = ( + contains(local.small_cc_instance, var.ccvm_instance_type) && var.cc_instance_size == "small" || + contains(local.medium_cc_instance, var.ccvm_instance_type) && var.cc_instance_size == "medium" || + contains(local.large_cc_instance, var.ccvm_instance_type) && var.cc_instance_size == "large" + ) +} + +variable "ccvm_image_publisher" { + type = string + description = "Azure Marketplace Cloud Connector Image Publisher" + default = "zscaler1579058425289" +} + +variable "ccvm_image_offer" { + type = string + description = "Azure Marketplace Cloud Connector Image Offer" + default = "zia_cloud_connector" +} + +variable "ccvm_image_sku" { + type = string + description = "Azure Marketplace Cloud Connector Image SKU" + default = "zs_ser_gen1_cc_01" +} + +variable "ccvm_image_version" { + type = string + description = "Azure Marketplace Cloud Connector Image Version" + default = "latest" +} + +variable "http_probe_port" { + type = number + description = "Port number for Cloud Connector cloud init to enable listener port for HTTP probe from Azure LB" + default = 50000 + validation { + condition = ( + tonumber(var.http_probe_port) == 80 || + (tonumber(var.http_probe_port) >= 1024 && tonumber(var.http_probe_port) <= 65535) + ) + error_message = "Input http_probe_port must be set to a single value of 80 or any number between 1024-65535." + } +} + +variable "workload_count" { + type = number + description = "The number of Workload VMs to deploy" + default = 1 + validation { + condition = var.workload_count >= 1 && var.workload_count <= 250 + error_message = "Input workload_count must be a whole number between 1 and 250." + } +} + +variable "cc_count" { + type = number + description = "The number of Cloud Connectors to deploy. Validation assumes max for /24 subnet but could be smaller or larger as long as subnet can accommodate" + default = 2 + validation { + condition = var.cc_count >= 1 && var.cc_count <= 250 + error_message = "Input cc_count must be a whole number between 1 and 250." + } +} + +variable "zones_enabled" { + type = bool + description = "Determine whether to provision Cloud Connector VMs explicitly in defined zones (if supported by the Azure region provided in the location variable). If left false, Azure will automatically choose a zone and module will create an availability set resource instead for VM fault tolerance" + default = false +} + +variable "zones" { + type = list(string) + description = "Specify which availability zone(s) to deploy VM resources in if zones_enabled variable is set to true" + default = ["1"] + validation { + condition = ( + !contains([for zones in var.zones : contains(["1", "2", "3"], zones)], false) + ) + error_message = "Input zones variable must be a number 1-3." + } +} + +variable "reuse_nsg" { + type = bool + description = "Specifies whether the NSG module should create 1:1 network security groups per instance or 1 network security group for all instances" + default = "false" +} + +variable "accelerated_networking_enabled" { + type = bool + description = "Enable/Disable accelerated networking support on all Cloud Connector service interfaces" + default = true +} + +variable "bastion_nsg_source_prefix" { + type = string + description = "user input for locking down SSH access to bastion to a specific IP or CIDR range" + default = "*" +} + +variable "load_distribution" { + type = string + description = "Azure LB load distribution method" + default = "Default" + validation { + condition = ( + var.load_distribution == "SourceIP" || + var.load_distribution == "SourceIPProtocol" || + var.load_distribution == "Default" + ) + error_message = "Input load_distribution must be set to either SourceIP, SourceIPProtocol, or Default." + } +} + +variable "lb_enabled" { + type = bool + description = "Default true. Only relevant for 'base' deployments. Configure Workload Route Table to default route next hop to the CC Load Balancer IP passed from var.lb_frontend_ip. If false, default route next hop directly to the CC Service IP passed from var.cc_service_ip" + default = true +} + +variable "health_check_interval" { + type = number + description = "The interval, in seconds, for how frequently to probe the endpoint for health status. Typically, the interval is slightly less than half the allocated timeout period (in seconds) which allows two full probes before taking the instance out of rotation. The default value is 15, the minimum value is 5" + default = 15 + validation { + condition = ( + var.health_check_interval > 4 + ) + error_message = "Input health_check_interval must be a number 5 or greater." + } +} + +variable "probe_threshold" { + type = number + description = "The number of consecutive successful or failed probes in order to allow or deny traffic from being delivered to this endpoint. After failing the number of consecutive probes equal to this value, the endpoint will be taken out of rotation and require the same number of successful consecutive probes to be placed back in rotation." + default = 2 +} + +variable "number_of_probes" { + type = number + description = "The number of probes where if no response, will result in stopping further traffic from being delivered to the endpoint. This values allows endpoints to be taken out of rotation faster or slower than the typical times used in Azure" + default = 1 +} + +variable "encryption_at_host_enabled" { + type = bool + description = "User input for enabling or disabling host encryption" + default = true +} + + +# Azure Private DNS specific variables +variable "zpa_enabled" { + type = bool + description = "Configure Azure Private DNS Outbound subnet, Resolvers, Rulesets/Rules, and Outbound Endpoint ZPA DNS redirection" + default = true +} + +variable "domain_names" { + type = map(any) + description = "Domain names fqdn/wildcard to have Azure Private DNS redirect DNS requests to Cloud Connector" +} + +variable "target_address" { + type = list(string) + description = "Azure DNS queries will be conditionally forwarded to these target IP addresses. Default are a pair of Zscaler Global VIP addresses" + default = ["185.46.212.88", "185.46.212.89"] +} diff --git a/examples/base_cc_lb_zpa/versions.tf b/examples/base_cc_lb_zpa/versions.tf new file mode 100755 index 0000000..82f3301 --- /dev/null +++ b/examples/base_cc_lb_zpa/versions.tf @@ -0,0 +1,36 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">= 3.46, <= 3.74" + } + random = { + source = "hashicorp/random" + version = "~> 3.3.0" + } + local = { + source = "hashicorp/local" + version = "~> 2.2.0" + } + null = { + source = "hashicorp/null" + version = "~> 3.1.0" + } + tls = { + source = "hashicorp/tls" + version = "~> 3.4.0" + } + } + required_version = ">= 0.13.7, < 2.0.0" +} + +provider "azurerm" { + features {} +} + +provider "azurerm" { + alias = "managed_identity_sub" + subscription_id = var.managed_identity_subscription_id == null ? var.env_subscription_id : var.managed_identity_subscription_id + features {} + skip_provider_registration = true +} diff --git a/examples/cc_lb/README.md b/examples/cc_lb/README.md index d26436d..4cde591 100644 --- a/examples/cc_lb/README.md +++ b/examples/cc_lb/README.md @@ -45,7 +45,7 @@ From cc_lb directory execute: | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 0.13.7, < 2.0.0 | -| [azurerm](#requirement\_azurerm) | ~> 3.46.0 | +| [azurerm](#requirement\_azurerm) | >= 3.46, <= 3.74 | | [local](#requirement\_local) | ~> 2.2.0 | | [null](#requirement\_null) | ~> 3.1.0 | | [random](#requirement\_random) | ~> 3.3.0 | @@ -69,6 +69,7 @@ From cc_lb directory execute: | [cc\_nsg](#module\_cc\_nsg) | ../../modules/terraform-zscc-nsg-azure | n/a | | [cc\_vm](#module\_cc\_vm) | ../../modules/terraform-zscc-ccvm-azure | n/a | | [network](#module\_network) | ../../modules/terraform-zscc-network-azure | n/a | +| [private\_dns](#module\_private\_dns) | ../../modules/terraform-zscc-private-dns-azure | n/a | ## Resources @@ -116,20 +117,28 @@ From cc_lb directory execute: | [ccvm\_image\_sku](#input\_ccvm\_image\_sku) | Azure Marketplace Cloud Connector Image SKU | `string` | `"zs_ser_gen1_cc_01"` | no | | [ccvm\_image\_version](#input\_ccvm\_image\_version) | Azure Marketplace Cloud Connector Image Version | `string` | `"latest"` | no | | [ccvm\_instance\_type](#input\_ccvm\_instance\_type) | Cloud Connector Image size | `string` | `"Standard_D2s_v3"` | no | +| [domain\_names](#input\_domain\_names) | Domain names fqdn/wildcard to have Azure Private DNS redirect DNS requests to Cloud Connector | `map(any)` | n/a | yes | +| [encryption\_at\_host\_enabled](#input\_encryption\_at\_host\_enabled) | User input for enabling or disabling host encryption | `bool` | `false` | no | | [env\_subscription\_id](#input\_env\_subscription\_id) | Azure Subscription ID where resources are to be deployed in | `string` | n/a | yes | | [environment](#input\_environment) | Customer defined environment tag. ie: Dev, QA, Prod, etc. | `string` | `"Development"` | no | | [existing\_nat\_gw\_pip\_association](#input\_existing\_nat\_gw\_pip\_association) | Set this to true only if both byo\_pips and byo\_nat\_gws variables are true. This implies that there are already NAT Gateway resources with Public IP Addresses associated so we do not attempt any new associations | `bool` | `false` | no | | [existing\_nat\_gw\_subnet\_association](#input\_existing\_nat\_gw\_subnet\_association) | Set this to true only if both byo\_nat\_gws and byo\_subnets variables are true. this implies that there are already NAT Gateway resources associated to subnets where Cloud Connectors are being deployed to | `bool` | `false` | no | +| [health\_check\_interval](#input\_health\_check\_interval) | The interval, in seconds, for how frequently to probe the endpoint for health status. Typically, the interval is slightly less than half the allocated timeout period (in seconds) which allows two full probes before taking the instance out of rotation. The default value is 15, the minimum value is 5 | `number` | `15` | no | | [http\_probe\_port](#input\_http\_probe\_port) | Port number for Cloud Connector cloud init to enable listener port for HTTP probe from Azure LB | `number` | `50000` | no | -| [load\_distribution](#input\_load\_distribution) | Azure LB load distribution method | `string` | `"SourceIP"` | no | +| [load\_distribution](#input\_load\_distribution) | Azure LB load distribution method | `string` | `"Default"` | no | | [managed\_identity\_subscription\_id](#input\_managed\_identity\_subscription\_id) | Azure Subscription ID where the User Managed Identity resource exists. Only required if this Subscription ID is different than env\_subscription\_id | `string` | `null` | no | -| [name\_prefix](#input\_name\_prefix) | The name prefix for all your resources | `string` | `"zsdemo"` | no | +| [name\_prefix](#input\_name\_prefix) | The name prefix for all your resources | `string` | `"zscc"` | no | | [network\_address\_space](#input\_network\_address\_space) | VNET CIDR / address prefix | `string` | `"10.1.0.0/16"` | no | +| [number\_of\_probes](#input\_number\_of\_probes) | The number of probes where if no response, will result in stopping further traffic from being delivered to the endpoint. This values allows endpoints to be taken out of rotation faster or slower than the typical times used in Azure | `number` | `1` | no | | [owner\_tag](#input\_owner\_tag) | Customer defined owner tag value. ie: Org, Dept, username, etc. | `string` | `"zscc-admin"` | no | +| [private\_dns\_subnet](#input\_private\_dns\_subnet) | Private DNS Resolver Outbound Endpoint Subnet to create in VNet. This is only required if you want to override the default subnet that this code creates via network\_address\_space variable. | `string` | `null` | no | +| [probe\_threshold](#input\_probe\_threshold) | The number of consecutive successful or failed probes in order to allow or deny traffic from being delivered to this endpoint. After failing the number of consecutive probes equal to this value, the endpoint will be taken out of rotation and require the same number of successful consecutive probes to be placed back in rotation. | `number` | `2` | no | | [reuse\_nsg](#input\_reuse\_nsg) | Specifies whether the NSG module should create 1:1 network security groups per instance or 1 network security group for all instances | `bool` | `"false"` | no | +| [target\_address](#input\_target\_address) | Azure DNS queries will be conditionally forwarded to these target IP addresses. Default are a pair of Zscaler Global VIP addresses | `list(string)` |
[
"185.46.212.88",
"185.46.212.89"
]
| no | | [tls\_key\_algorithm](#input\_tls\_key\_algorithm) | algorithm for tls\_private\_key resource | `string` | `"RSA"` | no | | [zones](#input\_zones) | Specify which availability zone(s) to deploy VM resources in if zones\_enabled variable is set to true | `list(string)` |
[
"1"
]
| no | | [zones\_enabled](#input\_zones\_enabled) | Determine whether to provision Cloud Connector VMs explicitly in defined zones (if supported by the Azure region provided in the location variable). If left false, Azure will automatically choose a zone and module will create an availability set resource instead for VM fault tolerance | `bool` | `false` | no | +| [zpa\_enabled](#input\_zpa\_enabled) | Configure Azure Private DNS Outbound subnet, Resolvers, Rulesets/Rules, and Outbound Endpoint ZPA DNS redirection | `bool` | `true` | no | ## Outputs diff --git a/examples/cc_lb/main.tf b/examples/cc_lb/main.tf index 764b2a0..9aaf427 100755 --- a/examples/cc_lb/main.tf +++ b/examples/cc_lb/main.tf @@ -53,8 +53,11 @@ module "network" { location = var.arm_location network_address_space = var.network_address_space cc_subnets = var.cc_subnets + private_dns_subnet = var.private_dns_subnet zones_enabled = var.zones_enabled zones = var.zones + lb_frontend_ip = module.cc_lb.lb_ip + zpa_enabled = var.zpa_enabled #bring-your-own variables byo_rg = var.byo_rg byo_rg_name = var.byo_rg_name @@ -88,6 +91,7 @@ locals { CC_URL=${var.cc_vm_prov_url} AZURE_VAULT_URL=${var.azure_vault_url} HTTP_PROBE_PORT=${var.http_probe_port} +AZURE_MANAGED_IDENTITY_CLIENT_ID=${module.cc_identity.managed_identity_client_id} USERDATA } @@ -124,6 +128,7 @@ module "cc_vm" { mgmt_nsg_id = module.cc_nsg.mgmt_nsg_id service_nsg_id = module.cc_nsg.service_nsg_id accelerated_networking_enabled = var.accelerated_networking_enabled + encryption_at_host_enabled = var.encryption_at_host_enabled depends_on = [ local_file.user_data_file, @@ -176,19 +181,56 @@ module "cc_identity" { # Health Probes ################################################################################ module "cc_lb" { - source = "../../modules/terraform-zscc-lb-azure" - name_prefix = var.name_prefix - resource_tag = random_string.suffix.result - global_tags = local.global_tags - resource_group = module.network.resource_group_name - location = var.arm_location - subnet_id = module.network.cc_subnet_ids[0] - http_probe_port = var.http_probe_port - load_distribution = var.load_distribution - zones_enabled = var.zones_enabled - zones = var.zones + source = "../../modules/terraform-zscc-lb-azure" + name_prefix = var.name_prefix + resource_tag = random_string.suffix.result + global_tags = local.global_tags + resource_group = module.network.resource_group_name + location = var.arm_location + subnet_id = module.network.cc_subnet_ids[0] + http_probe_port = var.http_probe_port + load_distribution = var.load_distribution + zones_enabled = var.zones_enabled + zones = var.zones + health_check_interval = var.health_check_interval + probe_threshold = var.probe_threshold + number_of_probes = var.number_of_probes +} + + +################################################################################ +# 6. Create Azure Private DNS Resolver Ruleset, Rules, and Outbound Endpoint +# for utilization with DNS redirection/conditional forwarding to Cloud +# Connector to enabling ZPA and/or ZIA DNS control features. +# This can optionally be enabled/disabled per variable "zpa_enabled". +################################################################################ +module "private_dns" { + count = var.zpa_enabled ? 1 : 0 + source = "../../modules/terraform-zscc-private-dns-azure" + name_prefix = var.name_prefix + resource_tag = random_string.suffix.result + global_tags = local.global_tags + resource_group = module.network.resource_group_name + location = var.arm_location + vnet_id = module.network.virtual_network_id + private_dns_subnet_id = module.network.private_dns_subnet_id + domain_names = var.domain_names + target_address = var.target_address } +################################################################################ +# Optional: Example create Azure Private DNS Resolver Virtual Network Link +# variable spoke_vnets does not exist in this deployment. This is simply +# an example of how you may utilize the private_dns module to create +# virtual network links for spoke VNets +################################################################################ +#resource "azurerm_private_dns_resolver_virtual_network_link" "dns_vnet_link" { +# count = length(var.spoke_vnets) +# name = "${var.name_prefix}-vnet-link-${count.index + 1}-${random_string.suffix.result}" +# dns_forwarding_ruleset_id = module.private_dns.private_dns_forwarding_ruleset_id +# virtual_network_id = element(var.spoke_vnets, count.index) +#} + ################################################################################ # Validation for Cloud Connector instance size and VM Instance Type diff --git a/examples/cc_lb/outputs.tf b/examples/cc_lb/outputs.tf index 667b907..1580267 100755 --- a/examples/cc_lb/outputs.tf +++ b/examples/cc_lb/outputs.tf @@ -1,6 +1,20 @@ locals { testbedconfig = < zones. ## Not configurable for base or base_1cc deployment types. (All others - Default: 2) ## E.g. cc_count set to 4 and 2 zones set ['1","2"] will create 2x CCs in AZ1 and 2x CCs in AZ2 #cc_count = 2 - -## 11. By default, no zones are specified in any resource creation meaning they are either auto-assigned by Azure +## 13. By default, no zones are specified in any resource creation meaning they are either auto-assigned by Azure ## (Virtual Machines and NAT Gateways) or Zone-Redundant (Public IP) based on whatever default configuration is. ## Setting this value to true will do the following: ## 1. will create zonal NAT Gateway resources in order of the zones [1-3] specified in zones variable. 1x per zone @@ -101,8 +103,7 @@ #zones_enabled = true - -## 12. By default, this variable is used as a count (1) for resource creation of Public IP, NAT Gateway, and CC Subnets. +## 14. By default, this variable is used as a count (1) for resource creation of Public IP, NAT Gateway, and CC Subnets. ## This should only be modified if zones_enabled is also set to true ## Doing so will change the default zone aware configuration for the 3 aforementioned resources with the values specified ## @@ -117,8 +118,7 @@ #zones = ["1","2"] #zones = ["1","2","3"] - -## 13. Network Configuration: +## 15. Network Configuration: ## IPv4 CIDR configured with VNet creation. All Subnet resources (Workload, Public, and Cloud Connector) will be created based off this prefix ## /24 subnets are created assuming this cidr is a /16. If you require creating a VNet smaller than /16, you may need to explicitly define all other @@ -141,56 +141,64 @@ #public_subnets = ["10.x.y.z/24","10.x.y.z/24"] #workloads_subnets = ["10.x.y.z/24","10.x.y.z/24"] #cc_subnets = ["10.x.y.z/24","10.x.y.z/24"] +#private_dns_subnet = "10.x.y.z/28" +## 16. Number of Workload VMs to be provisioned in the workload subnet. Only limitation is available IP space +## in subnet configuration. Only applicable for "base" deployment types. Default workload subnet is /24 so 250 max -## 15. Tag attribute "Owner" assigned to all resoure creation. (Default: "zscc-admin") +#workload_count = 2 -#owner_tag = "username@company.com" +## 17. Tag attribute "Owner" assigned to all resoure creation. (Default: "zscc-admin") +#owner_tag = "username@company.com" -## 16. Tag attribute "Environment" assigned to all resources created. (Default: "Development") +## 18. Tag attribute "Environment" assigned to all resources created. (Default: "Development") #environment = "Development" - -## 17. By default, this script will apply 1 Network Security Group per Cloud Connector instance. +## 19. By default, this script will apply 1 Network Security Group per Cloud Connector instance. ## Uncomment if you want to use the same Network Security Group for ALL Cloud Connectors (true or false. Default: false) #reuse_nsg = true +## 20. By default, Host encryption is enabled for Cloud Connector VMs. This does require the EncryptionAtHost feature +## enabled for your subscription though first. +## You can verify this by following the Azure Prerequisites guide here: +## https://learn.microsoft.com/en-us/azure/virtual-machines/linux/disks-enable-host-based-encryption-cli#prerequisites +## +## Uncomment if you want to not enable this VM setting + +#encryption_at_host_enabled = false + + ##################################################################################################################### ##### Custom BYO variables. Only applicable for "cc_lb" deployment without "base" resource requirements ##### ##################################################################################################################### -## 18. By default, this script will create a new Resource Group and place all resources in this group. +## 21. By default, this script will create a new Resource Group and place all resources in this group. ## Uncomment if you want to deploy all resources in an existing Resource Group? (true or false. Default: false) #byo_rg = true - -## 19. Provide your existing Resource Group name. Only uncomment and modify if you set byo_rg to true +## 22. Provide your existing Resource Group name. Only uncomment and modify if you set byo_rg to true #byo_rg_name = "existing-rg" - -## 20. By default, this script will create a new Azure Virtual Network in the default resource group. +## 23. By default, this script will create a new Azure Virtual Network in the default resource group. ## Uncomment if you want to deploy all resources to a VNet that already exists (true or false. Default: false) #byo_vnet = true - -## 21. Provide your existing VNet name. Only uncomment and modify if you set byo_vnet to true +## 24. Provide your existing VNet name. Only uncomment and modify if you set byo_vnet to true #byo_vnet_name = "existing-vnet" - -## 22. Provide the existing Resource Group name of your VNet. Only uncomment and modify if you set byo_vnet to true +## 25. Provide the existing Resource Group name of your VNet. Only uncomment and modify if you set byo_vnet to true ## Subnets depend on VNet so the same resource group is implied for subnets #byo_vnet_subnets_rg_name = "existing-vnet-rg" - -## 23. By default, this script will create 1 new Azure subnet in the default resource group unles the zones variable +## 26. By default, this script will create 1 new Azure subnet in the default resource group unles the zones variable ## specifies multiple zonal deployments in which case subnet 1 would logically map to resources in zone "1", etc. ## Uncomment if you want to deploy all resources in subnets that already exist (true or false. Default: false) ## Dependencies require in order to reference existing subnets, the corresponding VNet must also already exist. @@ -198,8 +206,7 @@ #byo_subnets = true - -## 24. Provide your existing Cloud Connector subnet names. Only uncomment and modify if you set byo_subnets to true +## 27. Provide your existing Cloud Connector subnet names. Only uncomment and modify if you set byo_subnets to true ## By default, management and service interfaces reside in a single subnet. Therefore, specifying multiple subnets ## implies only that you are doing a zonal deployment with resources in separate AZs and corresponding zonal NAT ## Gateway resources associated with the CC subnets mapped to the same respective zones. @@ -208,14 +215,12 @@ #byo_subnet_names = ["existing-cc-subnet"] - -## 25. By default, this script will create new Public IP resources to be associated with CC NAT Gateways. +## 28. By default, this script will create new Public IP resources to be associated with CC NAT Gateways. ## Uncomment if you want to use your own public IP for the NAT GW (true or false. Default: false) #byo_pips = true - -## 26. Provide your existing Azure Public IP resource names. Only uncomment and modify if you set byo_pips to true +## 29. Provide your existing Azure Public IP resource names. Only uncomment and modify if you set byo_pips to true ## Existing Public IP resource cannot be associated with any resource other than an existing NAT Gateway in which ## case existing_pip_association and existing_nat_gw_association need both set to true ## @@ -226,19 +231,16 @@ #byo_pip_names = ["pip-az1","pip-az2"] - -## 27. Provide the existing Resource Group name of your Azure public IPs. Only uncomment and modify if you set byo_pips to true +## 30. Provide the existing Resource Group name of your Azure public IPs. Only uncomment and modify if you set byo_pips to true #byo_pip_rg = "existing-pip-rg" - -## 28. By default, this script will create new NAT Gateway resources for the Cloud Connector subnets to be associated +## 31. By default, this script will create new NAT Gateway resources for the Cloud Connector subnets to be associated ## Uncomment if you want to use your own NAT Gateway (true or false. Default: false) #byo_nat_gws = true - -## 29. Provide your existing Azure NAT Gateway resource names. Only uncomment and modify if you set byo_nat_gws to true +## 32. Provide your existing Azure NAT Gateway resource names. Only uncomment and modify if you set byo_nat_gws to true ## ***** Note ***** ## If you already have existing NAT Gateways AND set zone_enabled to true these resource should be configured as zonal and ## be added here to this variable list in order of the zones specified in the "zones" variable. @@ -246,35 +248,30 @@ #byo_nat_gw_names = ["natgw-az1","natgw-az2"] - -## 30. Provide the existing Resource Group name of your NAT Gateway. Only uncomment and modify if you set byo_nat_gws to true +## 33. Provide the existing Resource Group name of your NAT Gateway. Only uncomment and modify if you set byo_nat_gws to true #byo_nat_gw_rg = "existing-nat-gw-rg" - -## 31. By default, this script will create a new Azure Public IP and associate it with new/existing NAT Gateways. +## 34. By default, this script will create a new Azure Public IP and associate it with new/existing NAT Gateways. ## Uncomment if you are deploying cloud connector to an environment where the PIP already exists AND is already asssociated to ## an existing NAT Gateway. (true or false. Default: false). ## Setting existing_pip_association to true means byo_nat_gws and byo_pips must ALSO be set to true. #existing_nat_gw_pip_association = true - -## 32. By default this script will create a new Azure NAT Gateway and associate it with new or existing CC subnets. +## 35. By default this script will create a new Azure NAT Gateway and associate it with new or existing CC subnets. ## Uncomment if you are deploying cloud connector to an environment where the subnet already exists AND is already asssociated to ## an existing NAT Gateway. (true or false. Default: false). ## Setting existing_nat_gw_association to true means byo_subnets AND byo_nat_gws must also be set to true. #existing_nat_gw_subnet_association = true - -## 33. By default, this script will create new Network Security Groups for the Cloud Connector mgmt and service interfaces +## 36. By default, this script will create new Network Security Groups for the Cloud Connector mgmt and service interfaces ## Uncomment if you want to use your own NSGs (true or false. Default: false) #byo_nsg = true - -## 34. Provide your existing Network Security Group resource names. Only uncomment and modify if you set byo_nsg to true +## 37. Provide your existing Network Security Group resource names. Only uncomment and modify if you set byo_nsg to true ## ***** Note ***** ## Example: byo_mgmt_nsg_names = ["mgmt-nsg-1","mgmt-nsg-2"] @@ -283,7 +280,27 @@ #byo_mgmt_nsg_names = ["mgmt-nsg-1","mgmt-nsg-2"] #byo_service_nsg_names = ["service-nsg-1","service-nsg-2"] - -## 35. Provide the existing Resource Group name of your Network Security Groups. Only uncomment and modify if you set byo_nsg to true +## 38. Provide the existing Resource Group name of your Network Security Groups. Only uncomment and modify if you set byo_nsg to true #byo_nsg_rg = "existing-nsg-rg" + + +##################################################################################################################### +##### ZPA/Azure Private DNS specific variables ##### +##################################################################################################################### +## 39. Provide the domain names you want Azure Private DNS to redirect to Cloud Connector for ZPA interception. +## Only applicable for base + zpa or zpa_enabled = true deployment types where Outbound DNS subnets, Resolver Ruleset/Rules, +## and Outbound Endpoints are being created. Two example domains are populated to show the mapping structure and syntax. +## Azure does require a trailing dot "." on all domain entries. ZPA Module will read through each to create a resolver rule per +## domain_names entry. Ucomment domain_names variable and add any additional appsegXX mappings as needed. + +#domain_names = { +# appseg1 = "app1.com." +# appseg2 = "app2.com." +#} + +## 40. Azure Private DNS queries will be conditionally forwarded to these target IP addresses. Default are a pair of Zscaler Global VIP addresses. +## The required expectation is that the target should follow VNet/subnet routing towards the configured Cloud Connector Load Balancer VIP for +## ZPA DNS interception + +#target_address = ["185.46.212.88", "185.46.212.89"] diff --git a/examples/cc_lb/variables.tf b/examples/cc_lb/variables.tf index 69a52a5..b2d0662 100755 --- a/examples/cc_lb/variables.tf +++ b/examples/cc_lb/variables.tf @@ -13,7 +13,11 @@ variable "arm_location" { variable "name_prefix" { type = string description = "The name prefix for all your resources" - default = "zsdemo" + default = "zscc" + validation { + condition = length(var.name_prefix) <= 12 + error_message = "Variable name_prefix must be 12 or less characters." + } } variable "network_address_space" { @@ -46,6 +50,12 @@ variable "cc_subnets" { default = null } +variable "private_dns_subnet" { + type = string + description = "Private DNS Resolver Outbound Endpoint Subnet to create in VNet. This is only required if you want to override the default subnet that this code creates via network_address_space variable." + default = null +} + variable "managed_identity_subscription_id" { type = string description = "Azure Subscription ID where the User Managed Identity resource exists. Only required if this Subscription ID is different than env_subscription_id" @@ -196,7 +206,7 @@ variable "accelerated_networking_enabled" { variable "load_distribution" { type = string description = "Azure LB load distribution method" - default = "SourceIP" + default = "Default" validation { condition = ( var.load_distribution == "SourceIP" || @@ -207,6 +217,55 @@ variable "load_distribution" { } } +variable "health_check_interval" { + type = number + description = "The interval, in seconds, for how frequently to probe the endpoint for health status. Typically, the interval is slightly less than half the allocated timeout period (in seconds) which allows two full probes before taking the instance out of rotation. The default value is 15, the minimum value is 5" + default = 15 + validation { + condition = ( + var.health_check_interval > 4 + ) + error_message = "Input health_check_interval must be a number 5 or greater." + } +} + +variable "probe_threshold" { + type = number + description = "The number of consecutive successful or failed probes in order to allow or deny traffic from being delivered to this endpoint. After failing the number of consecutive probes equal to this value, the endpoint will be taken out of rotation and require the same number of successful consecutive probes to be placed back in rotation." + default = 2 +} + +variable "number_of_probes" { + type = number + description = "The number of probes where if no response, will result in stopping further traffic from being delivered to the endpoint. This values allows endpoints to be taken out of rotation faster or slower than the typical times used in Azure" + default = 1 +} + +variable "encryption_at_host_enabled" { + type = bool + description = "User input for enabling or disabling host encryption" + default = false +} + + +# Azure Private DNS specific variables +variable "zpa_enabled" { + type = bool + description = "Configure Azure Private DNS Outbound subnet, Resolvers, Rulesets/Rules, and Outbound Endpoint ZPA DNS redirection" + default = true +} + +variable "domain_names" { + type = map(any) + description = "Domain names fqdn/wildcard to have Azure Private DNS redirect DNS requests to Cloud Connector" +} + +variable "target_address" { + type = list(string) + description = "Azure DNS queries will be conditionally forwarded to these target IP addresses. Default are a pair of Zscaler Global VIP addresses" + default = ["185.46.212.88", "185.46.212.89"] +} + ################################################################################ # BYO (Bring-your-own) variables list diff --git a/examples/cc_lb/versions.tf b/examples/cc_lb/versions.tf index e97eecf..82f3301 100755 --- a/examples/cc_lb/versions.tf +++ b/examples/cc_lb/versions.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "~> 3.46.0" + version = ">= 3.46, <= 3.74" } random = { source = "hashicorp/random" diff --git a/examples/zsec b/examples/zsec index 4a696cd..75ce9fa 100755 --- a/examples/zsec +++ b/examples/zsec @@ -29,6 +29,9 @@ if [[ "$oper" == "up" ]]; then case $deploy in greenfield) + echo "" + echo "**Caution** These deployments include test workloads and publicly accessible jump hosts and are intended primarily for lab/test environments" + echo "" break ;; brownfield) @@ -41,10 +44,10 @@ if [[ "$oper" == "up" ]]; then done while [ "$deploy" == "greenfield" ]; do - read -r -p "Deployment Type: ( base | base_1cc | base_cc_lb ) : " dtype + read -r -p "Deployment Type: ( base | base_1cc | base_1cc_zpa | base_cc_lb | base_cc_lb_zpa ) : " dtype case $dtype in - base|base_1cc|base_cc_lb|cc_lb) + base|base_1cc|base_1cc_zpa|base_cc_lb|base_cc_lb_zpa) echo "Deployment Type: ${dtype}" break ;; @@ -246,24 +249,76 @@ fi done fi - cc_instance_size_default=small - read -r -p "Enter CC Instance Size. Valid input = small, medium, or large. This needs to match the size chosen in the CC provisioning template [Default=$cc_instance_size_default]: " cc_instance_size_input -cc_instance_size=${cc_instance_size_input:-$cc_instance_size_default} - case $cc_instance_size in - small|medium|large) - echo "Cloud Connector size: ${cc_instance_size}" - echo "export TF_VAR_cc_instance_size=${cc_instance_size}" >> .zsecrc - ;; - *) - echo "Invalid Cloud Connector size: ${cc_instance_size}." - echo "Delete .zsecrc file and re-run zsecup ..." - exit 1 - ;; - esac + # --- + # Host Encryption Support + encryption_at_host_enabled=true + # Sourcing .zsecrc to use credentials during execution + source .zsecrc + + while true; do + read -r -p "Do you want to enable the Azure host encryption feature? [Default setting is Yes]: " input + + # Convert the user input to lowercase for case-insensitive comparison + input=$(echo "$input" | tr '[:upper:]' '[:lower:]') + + if [[ "$input" == "no" || "$input" == "n" ]]; then + echo "Setting encryption_at_host_enabled to false..." + encryption_at_host_enabled=false + break + elif [[ "$input" == "yes" || "$input" == "y" || -z "$input" ]]; then + echo "Checking if EncryptionAtHost feature is enabled for subscription $ARM_SUBSCRIPTION_ID..." + + TOKEN_ENDPOINT="https://login.microsoftonline.com/${ARM_TENANT_ID}/oauth2/token" + RESOURCE="https://management.azure.com/" + ACCESS_TOKEN=$(curl -s -X POST -H "Content-Type: application/x-www-form-urlencoded" \ + -d "grant_type=client_credentials" \ + -d "client_id=${ARM_CLIENT_ID}" \ + -d "client_secret=${ARM_CLIENT_SECRET}" \ + -d "resource=${RESOURCE}" \ + "${TOKEN_ENDPOINT}" | grep -o '"access_token":"[^"]*' | awk -F'"' '{print $4}') + + response=$(curl -s -X GET -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + "https://management.azure.com/subscriptions/$ARM_SUBSCRIPTION_ID/providers/Microsoft.Features/providers/Microsoft.Compute/features/EncryptionAtHost?api-version=2021-07-01" \ + |grep -o '"state":"[^"]*' | awk -F'"' '{print $4}') + + if [ "$response" = "Registered" ]; then + echo "EncryptionAtHost feature is enabled for subscription $ARM_SUBSCRIPTION_ID..." + echo "Setting encryption_at_host_enabled to true..." + encryption_at_host_enabled=true + else + echo "Error: Azure Subscription $ARM_SUBSCRIPTION_ID is not registered to support host encryption. Please refer to documentation." + exit 1 + fi + break + else + echo "Invalid input. Please enter 'yes' or 'no'." + fi + done + + echo "export TF_VAR_encryption_at_host_enabled=${encryption_at_host_enabled}" >> .zsecrc + # End of Host Encryption support update + # --- + + + cc_instance_size=small + echo "export TF_VAR_cc_instance_size=${cc_instance_size}" >> .zsecrc + #read -r -p "Enter CC Instance Size. Valid input = small, medium, or large. This needs to match the size chosen in the CC provisioning template [Default=$cc_instance_size_default]: " cc_instance_size_input +#cc_instance_size=${cc_instance_size_input:-$cc_instance_size_default} +# case $cc_instance_size in +# small|medium|large) +# echo "Cloud Connector size: ${cc_instance_size}" +# echo "export TF_VAR_cc_instance_size=${cc_instance_size}" >> .zsecrc +# ;; +# *) +# echo "Invalid Cloud Connector size: ${cc_instance_size}." +# echo "Delete .zsecrc file and re-run zsecup ..." +# exit 1 +# ;; +# esac ccvm_instance_type_default=Standard_D2s_v3 while true; do - read -r -p "Enter desired Azure VM type for CC. Recommended types: Small CC (Standard_D2s_v3); Medium (Standard_D8s_v3 or Standard_DS3_v2); Large (Standard_D16s_v3 or Standard_DS5_v2) [Default=$ccvm_instance_type_default]: " ccvm_instance_type_input + read -r -p "Enter desired Azure VM type for CC. Recommended types: Small CC (Standard_D2s_v3) [Default=$ccvm_instance_type_default]: " ccvm_instance_type_input ccvm_instance_type=${ccvm_instance_type_input:-$ccvm_instance_type_default} case $ccvm_instance_type in Standard_D2s_v3|Standard_DS3_v2|Standard_D8s_v3|Standard_D16s_v3|Standard_DS5_v2 ) @@ -448,6 +503,70 @@ else echo "Azure region ${azure_location} does not support Zones. Proceeding..." fi +if [[ "$dtype" == "cc"* ]]; then + while true; do + read -r -p "Enable Azure Private DNS for ZPA? (yes/no): " zpa_response +case $zpa_response in + yes|y ) + echo "Enabling Azure Private DNS module..." + zpa_enabled=true + echo "export TF_VAR_zpa_enabled=$zpa_enabled" >> .zsecrc + break + ;; + no|n ) + echo "No ZPA enablement..." + zpa_enabled=false + echo "export TF_VAR_zpa_enabled=$zpa_enabled" >> .zsecrc + break + ;; + * ) echo "invalid response. Please enter yes or no";; + esac +done +fi + +if [[ "$zpa_enabled" == "true" || "$dtype" == *"zpa" ]]; then +array=() +domain_names_map="'{ " +counter=0 +while true; do +read -r -p "How many Domain/FQDN application segments to add to Private DNS Resolver Rules? " domain_number +if [[ $domain_number == 0 ]]; then + echo "Invalid input. Please enter a whole number for the number of domains you will be adding..." +elif [[ $domain_number =~ ^[0-9]+$ ]]; then + echo "$domain_number domains to enter..." + break +else + echo "Invalid input. Please enter a whole number for the number of domains you will be adding..." +fi +done +for i in $(seq $domain_number); do +read -r -p "Enter a single ZPA Domain/FQDN ending with a trailing dot ( e.g. azure.company.com. ): " domain_name + if [[ $domain_name = *" "* ]]; then + echo "Spaces not allowed. Please enter only one domain at a time. Delete .zsecrc file and re-run zsec up..." + exit 1 + elif [[ $domain_name == '' ]]; then + echo "Empty entries are not allowed. Delete .zsecrc file and re-run zsec up..." + exit 1 + elif [[ $domain_name == "." ]]; then + echo "You entered '.' dot. While Azure does support this to forward all domain requests, this could have unintended consequences/compatibility issues with Azure services" + elif [[ $domain_name == "."* ]]; then + echo "Invalid format. Domains cannot start with a dot (.). Delete .zsecrc file and re-run zsec up..." + exit 1 + elif [[ $domain_name == "*"* ]]; then + echo "Invalid format. Domains cannot start with a star/wildcard (*). Delete .zsecrc file and re-run zsec up..." + exit 1 + elif [[ $domain_name != *"." ]]; then + echo "Invalid format. Domains must end with a dot (.). Delete .zsecrc file and re-run zsec up..." + exit 1 + fi +array+=("$domain_name") + counter=$(( $counter + 1 )) + domain_names_map+="appseg$counter: \"$domain_name\", " +done +domain_names_map+="}'" +echo "export TF_VAR_domain_names=$domain_names_map" >> .zsecrc +fi + fi diff --git a/modules/terraform-zscc-bastion-azure/README.md b/modules/terraform-zscc-bastion-azure/README.md index 2a880af..ad2100e 100644 --- a/modules/terraform-zscc-bastion-azure/README.md +++ b/modules/terraform-zscc-bastion-azure/README.md @@ -8,13 +8,13 @@ This module creates all Azure VM, NSG, and Public IP resources needed to deploy | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 0.13.7, < 2.0.0 | -| [azurerm](#requirement\_azurerm) | ~> 3.46.0 | +| [azurerm](#requirement\_azurerm) | >= 3.46, <= 3.74 | ## Providers | Name | Version | |------|---------| -| [azurerm](#provider\_azurerm) | ~> 3.46.0 | +| [azurerm](#provider\_azurerm) | >= 3.46, <= 3.74 | ## Modules diff --git a/modules/terraform-zscc-bastion-azure/versions.tf b/modules/terraform-zscc-bastion-azure/versions.tf index 0dc1d32..903cd9b 100755 --- a/modules/terraform-zscc-bastion-azure/versions.tf +++ b/modules/terraform-zscc-bastion-azure/versions.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "~> 3.46.0" + version = ">= 3.46, <= 3.74" } } required_version = ">= 0.13.7, < 2.0.0" diff --git a/modules/terraform-zscc-ccvm-azure/README.md b/modules/terraform-zscc-ccvm-azure/README.md index 56c652e..cc8504e 100644 --- a/modules/terraform-zscc-ccvm-azure/README.md +++ b/modules/terraform-zscc-ccvm-azure/README.md @@ -21,7 +21,7 @@ az vm image terms accept --urn zscaler1579058425289:zia_cloud_connector:zs_ser_g | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 0.13.7, < 2.0.0 | -| [azurerm](#requirement\_azurerm) | ~> 3.46.0 | +| [azurerm](#requirement\_azurerm) | >= 3.46, <= 3.74 | | [local](#requirement\_local) | ~> 2.2.0 | | [null](#requirement\_null) | ~> 3.1.0 | @@ -29,7 +29,7 @@ az vm image terms accept --urn zscaler1579058425289:zia_cloud_connector:zs_ser_g | Name | Version | |------|---------| -| [azurerm](#provider\_azurerm) | ~> 3.46.0 | +| [azurerm](#provider\_azurerm) | >= 3.46, <= 3.74 | | [null](#provider\_null) | ~> 3.1.0 | ## Modules @@ -47,9 +47,6 @@ No modules. | [azurerm_network_interface.cc_service_nic_1](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface) | resource | | [azurerm_network_interface.cc_service_nic_2](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface) | resource | | [azurerm_network_interface.cc_service_nic_3](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface) | resource | -| [azurerm_network_interface_backend_address_pool_association.cc_vm_service_1_nic_lb_association](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface_backend_address_pool_association) | resource | -| [azurerm_network_interface_backend_address_pool_association.cc_vm_service_2_nic_lb_association](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface_backend_address_pool_association) | resource | -| [azurerm_network_interface_backend_address_pool_association.cc_vm_service_3_nic_lb_association](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface_backend_address_pool_association) | resource | | [azurerm_network_interface_backend_address_pool_association.cc_vm_service_nic_lb_association](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface_backend_address_pool_association) | resource | | [azurerm_network_interface_security_group_association.cc_mgmt_nic_association](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface_security_group_association) | resource | | [azurerm_network_interface_security_group_association.cc_service_nic_1_association](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/network_interface_security_group_association) | resource | @@ -72,6 +69,7 @@ No modules. | [ccvm\_image\_sku](#input\_ccvm\_image\_sku) | Azure Marketplace Cloud Connector Image SKU | `string` | `"zs_ser_gen1_cc_01"` | no | | [ccvm\_image\_version](#input\_ccvm\_image\_version) | Azure Marketplace Cloud Connector Image Version | `string` | `"latest"` | no | | [ccvm\_instance\_type](#input\_ccvm\_instance\_type) | Cloud Connector Image size | `string` | `"Standard_D2s_v3"` | no | +| [encryption\_at\_host\_enabled](#input\_encryption\_at\_host\_enabled) | User input for enabling or disabling host encryption | `bool` | `true` | no | | [global\_tags](#input\_global\_tags) | Populate any custom user defined tags from a map | `map(string)` | `{}` | no | | [lb\_association\_enabled](#input\_lb\_association\_enabled) | Determines whether or not to create a nic backend pool assocation to the service nic(s) | `bool` | `false` | no | | [location](#input\_location) | Cloud Connector Azure Region | `string` | n/a | yes | diff --git a/modules/terraform-zscc-ccvm-azure/main.tf b/modules/terraform-zscc-ccvm-azure/main.tf index 32fb89b..e4d12b5 100755 --- a/modules/terraform-zscc-ccvm-azure/main.tf +++ b/modules/terraform-zscc-ccvm-azure/main.tf @@ -9,7 +9,7 @@ resource "azurerm_network_interface" "cc_mgmt_nic" { resource_group_name = var.resource_group ip_configuration { - name = "${var.name_prefix}-cc-mgmt-nic-conf-${var.resource_tag}" + name = "${var.name_prefix}-ccvm-mgmt-nic-conf-${var.resource_tag}" subnet_id = element(var.mgmt_subnet_id, count.index) private_ip_address_allocation = "Dynamic" primary = true @@ -37,14 +37,14 @@ resource "azurerm_network_interface_security_group_association" "cc_mgmt_nic_ass ################################################################################ resource "azurerm_network_interface" "cc_service_nic" { count = local.valid_cc_create ? var.cc_count : 0 - name = var.cc_instance_size == "small" ? "${var.name_prefix}-ccvm-${count.index + 1}-service-nic-${var.resource_tag}" : "${var.name_prefix}-ccvm-${count.index + 1}-lb-nic-${var.resource_tag}" + name = "${var.name_prefix}-ccvm-${count.index + 1}-fwd-nic-${var.resource_tag}" location = var.location resource_group_name = var.resource_group enable_ip_forwarding = true enable_accelerated_networking = var.accelerated_networking_enabled ip_configuration { - name = var.cc_instance_size == "small" ? "${var.name_prefix}-cc-service-nic-conf-${var.resource_tag}" : "${var.name_prefix}-cc-lb-nic-conf-${var.resource_tag}" + name = "${var.name_prefix}-ccvm-fwd-nic-conf-${var.resource_tag}" subnet_id = element(var.service_subnet_id, count.index) private_ip_address_allocation = "Dynamic" primary = true @@ -57,7 +57,7 @@ resource "azurerm_network_interface" "cc_service_nic" { ################################################################################ -# Associate CC Service/LB NIC to Service NSG +# Associate CC Service/Forwarding NIC to Service NSG ################################################################################ resource "azurerm_network_interface_security_group_association" "cc_service_nic_association" { count = local.valid_cc_create ? var.cc_count : 0 @@ -182,41 +182,11 @@ resource "azurerm_network_interface_security_group_association" "cc_service_nic_ ################################################################################ # Create Cloud Connector Network Interface to Load Balancer associations ################################################################################ -# Associate "small" CC service interface to Azure LB backend pool. This resource will not be created for "medium" or "large" CC instances +# Associate CC forwarding interface to Azure LB backend pool resource "azurerm_network_interface_backend_address_pool_association" "cc_vm_service_nic_lb_association" { count = var.lb_association_enabled == true && var.cc_instance_size == "small" ? var.cc_count : 0 network_interface_id = azurerm_network_interface.cc_service_nic[count.index].id - ip_configuration_name = "${var.name_prefix}-cc-service-nic-conf-${var.resource_tag}" - backend_address_pool_id = var.backend_address_pool - - depends_on = [var.backend_address_pool] -} - -# Associate "medium/large" CC service interface-1 to Azure LB backend pool. This resource will not be created for "small" CC instances -resource "azurerm_network_interface_backend_address_pool_association" "cc_vm_service_1_nic_lb_association" { - count = var.lb_association_enabled == true && var.cc_instance_size != "small" ? var.cc_count : 0 - network_interface_id = azurerm_network_interface.cc_service_nic_1[count.index].id - ip_configuration_name = "${var.name_prefix}-cc-service-nic-1-conf-${var.resource_tag}" - backend_address_pool_id = var.backend_address_pool - - depends_on = [var.backend_address_pool] -} - -# Associate "medium/large" CC service interface-2 to Azure LB backend pool. This resource will not be created for "small" CC instances -resource "azurerm_network_interface_backend_address_pool_association" "cc_vm_service_2_nic_lb_association" { - count = var.lb_association_enabled == true && var.cc_instance_size != "small" ? var.cc_count : 0 - network_interface_id = azurerm_network_interface.cc_service_nic_2[count.index].id - ip_configuration_name = "${var.name_prefix}-cc-service-nic-2-conf-${var.resource_tag}" - backend_address_pool_id = var.backend_address_pool - - depends_on = [var.backend_address_pool] -} - -# Associate "large" CC service interface-3 to Azure LB backend pool. This resource will not be created for "small" or "medium" CC instances -resource "azurerm_network_interface_backend_address_pool_association" "cc_vm_service_3_nic_lb_association" { - count = var.lb_association_enabled == true && var.cc_instance_size == "large" ? var.cc_count : 0 - network_interface_id = azurerm_network_interface.cc_service_nic_3[count.index].id - ip_configuration_name = "${var.name_prefix}-cc-service-nic-3-conf-${var.resource_tag}" + ip_configuration_name = "${var.name_prefix}-ccvm-fwd-nic-conf-${var.resource_tag}" backend_address_pool_id = var.backend_address_pool depends_on = [var.backend_address_pool] @@ -227,13 +197,14 @@ resource "azurerm_network_interface_backend_address_pool_association" "cc_vm_ser # Create Cloud Connector VM ################################################################################ resource "azurerm_linux_virtual_machine" "cc_vm" { - count = local.valid_cc_create ? var.cc_count : 0 - name = "${var.name_prefix}-ccvm-${count.index + 1}-${var.resource_tag}" - location = var.location - resource_group_name = var.resource_group - size = var.ccvm_instance_type - availability_set_id = local.zones_supported == false ? azurerm_availability_set.cc_availability_set[0].id : null - zone = local.zones_supported ? element(var.zones, count.index) : null + count = local.valid_cc_create ? var.cc_count : 0 + name = "${var.name_prefix}-ccvm-${count.index + 1}-${var.resource_tag}" + location = var.location + resource_group_name = var.resource_group + size = var.ccvm_instance_type + availability_set_id = local.zones_supported == false ? azurerm_availability_set.cc_availability_set[0].id : null + zone = local.zones_supported ? element(var.zones, count.index) : null + encryption_at_host_enabled = var.encryption_at_host_enabled # Cloud Connector requires that the ordering of network_interface_ids associated are #1/mgmt, #2/service (or lb for med/lrg CC), #3/service-1, #4/service-2, #5/service-3 network_interface_ids = [ diff --git a/modules/terraform-zscc-ccvm-azure/variables.tf b/modules/terraform-zscc-ccvm-azure/variables.tf index 5f056d7..c05ca52 100755 --- a/modules/terraform-zscc-ccvm-azure/variables.tf +++ b/modules/terraform-zscc-ccvm-azure/variables.tf @@ -198,3 +198,8 @@ locals { contains(local.max_fd_supported_regions, var.location) && var.zones_enabled == false ) } +variable "encryption_at_host_enabled" { + type = bool + description = "User input for enabling or disabling host encryption" + default = true +} diff --git a/modules/terraform-zscc-ccvm-azure/versions.tf b/modules/terraform-zscc-ccvm-azure/versions.tf index 2f497b3..5c56d84 100755 --- a/modules/terraform-zscc-ccvm-azure/versions.tf +++ b/modules/terraform-zscc-ccvm-azure/versions.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "~> 3.46.0" + version = ">= 3.46, <= 3.74" } local = { source = "hashicorp/local" diff --git a/modules/terraform-zscc-identity-azure/README.md b/modules/terraform-zscc-identity-azure/README.md index f3f94de..a3a0cb1 100644 --- a/modules/terraform-zscc-identity-azure/README.md +++ b/modules/terraform-zscc-identity-azure/README.md @@ -8,13 +8,13 @@ This module takes name and resource group inputs of an existing User Managed Ide | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 0.13.7, < 2.0.0 | -| [azurerm](#requirement\_azurerm) | ~> 3.46.0 | +| [azurerm](#requirement\_azurerm) | >= 3.46, <= 3.74 | ## Providers | Name | Version | |------|---------| -| [azurerm](#provider\_azurerm) | ~> 3.46.0 | +| [azurerm](#provider\_azurerm) | >= 3.46, <= 3.74 | ## Modules @@ -37,5 +37,6 @@ No modules. | Name | Description | |------|-------------| +| [managed\_identity\_client\_id](#output\_managed\_identity\_client\_id) | The Client ID of the User Assigned Identity | | [managed\_identity\_id](#output\_managed\_identity\_id) | User Managed Identity ID | diff --git a/modules/terraform-zscc-identity-azure/outputs.tf b/modules/terraform-zscc-identity-azure/outputs.tf index 896f680..f482d2a 100755 --- a/modules/terraform-zscc-identity-azure/outputs.tf +++ b/modules/terraform-zscc-identity-azure/outputs.tf @@ -2,3 +2,8 @@ output "managed_identity_id" { description = "User Managed Identity ID" value = data.azurerm_user_assigned_identity.selected.id } + +output "managed_identity_client_id" { + description = "The Client ID of the User Assigned Identity" + value = data.azurerm_user_assigned_identity.selected.client_id +} diff --git a/modules/terraform-zscc-identity-azure/versions.tf b/modules/terraform-zscc-identity-azure/versions.tf index 0dc1d32..903cd9b 100755 --- a/modules/terraform-zscc-identity-azure/versions.tf +++ b/modules/terraform-zscc-identity-azure/versions.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "~> 3.46.0" + version = ">= 3.46, <= 3.74" } } required_version = ">= 0.13.7, < 2.0.0" diff --git a/modules/terraform-zscc-lb-azure/README.md b/modules/terraform-zscc-lb-azure/README.md index 7c3e363..7d57651 100644 --- a/modules/terraform-zscc-lb-azure/README.md +++ b/modules/terraform-zscc-lb-azure/README.md @@ -8,13 +8,13 @@ This module creates a Standard Load Balancer, backend addres pool, LB rules, and | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 0.13.7, < 2.0.0 | -| [azurerm](#requirement\_azurerm) | ~> 3.46.0 | +| [azurerm](#requirement\_azurerm) | >= 3.46, <= 3.74 | ## Providers | Name | Version | |------|---------| -| [azurerm](#provider\_azurerm) | ~> 3.46.0 | +| [azurerm](#provider\_azurerm) | >= 3.46, <= 3.74 | ## Modules @@ -34,10 +34,13 @@ No modules. | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [global\_tags](#input\_global\_tags) | Populate any custom user defined tags from a map | `map(string)` | `{}` | no | +| [health\_check\_interval](#input\_health\_check\_interval) | The interval, in seconds, for how frequently to probe the endpoint for health status. Typically, the interval is slightly less than half the allocated timeout period (in seconds) which allows two full probes before taking the instance out of rotation. The default value is 15, the minimum value is 5 | `number` | `15` | no | | [http\_probe\_port](#input\_http\_probe\_port) | Port number for Cloud Connector cloud init to enable listener port for HTTP probe from Azure LB | `number` | `50000` | no | -| [load\_distribution](#input\_load\_distribution) | Azure LB load distribution method | `string` | `"SourceIP"` | no | +| [load\_distribution](#input\_load\_distribution) | Azure LB load distribution method | `string` | `"Default"` | no | | [location](#input\_location) | Cloud Connector Azure Region | `string` | n/a | yes | | [name\_prefix](#input\_name\_prefix) | A prefix to associate to all the lb module resources | `string` | `null` | no | +| [number\_of\_probes](#input\_number\_of\_probes) | The number of probes where if no response, will result in stopping further traffic from being delivered to the endpoint. This values allows endpoints to be taken out of rotation faster or slower than the typical times used in Azure | `number` | `1` | no | +| [probe\_threshold](#input\_probe\_threshold) | The number of consecutive successful or failed probes in order to allow or deny traffic from being delivered to this endpoint. After failing the number of consecutive probes equal to this value, the endpoint will be taken out of rotation and require the same number of successful consecutive probes to be placed back in rotation. | `number` | `2` | no | | [resource\_group](#input\_resource\_group) | Main Resource Group Name | `string` | n/a | yes | | [resource\_tag](#input\_resource\_tag) | A tag to associate to all the lb module resources | `string` | `null` | no | | [subnet\_id](#input\_subnet\_id) | Subnet ID for LB Frontend IP placement | `string` | n/a | yes | diff --git a/modules/terraform-zscc-lb-azure/main.tf b/modules/terraform-zscc-lb-azure/main.tf index 870ee25..146b9c8 100755 --- a/modules/terraform-zscc-lb-azure/main.tf +++ b/modules/terraform-zscc-lb-azure/main.tf @@ -44,11 +44,14 @@ resource "azurerm_lb_backend_address_pool" "cc_lb_backend_pool" { # Define load balancer health probe parameters ################################################################################ resource "azurerm_lb_probe" "cc_lb_probe" { - name = "${var.name_prefix}-cc-lb-probe-${var.resource_tag}" - loadbalancer_id = azurerm_lb.cc_lb.id - protocol = "Http" - port = var.http_probe_port - request_path = "/?cchealth" + name = "${var.name_prefix}-cc-lb-probe-${var.resource_tag}" + loadbalancer_id = azurerm_lb.cc_lb.id + protocol = "Http" + port = var.http_probe_port + request_path = "/?cchealth" + interval_in_seconds = var.health_check_interval + probe_threshold = var.probe_threshold + number_of_probes = var.number_of_probes } diff --git a/modules/terraform-zscc-lb-azure/variables.tf b/modules/terraform-zscc-lb-azure/variables.tf index e84026a..87d39f8 100755 --- a/modules/terraform-zscc-lb-azure/variables.tf +++ b/modules/terraform-zscc-lb-azure/variables.tf @@ -47,7 +47,7 @@ variable "http_probe_port" { variable "load_distribution" { type = string description = "Azure LB load distribution method" - default = "SourceIP" + default = "Default" validation { condition = ( var.load_distribution == "SourceIP" || @@ -84,3 +84,27 @@ variable "zones" { error_message = "Input zones variable must be a number 1-3." } } + +variable "health_check_interval" { + type = number + description = "The interval, in seconds, for how frequently to probe the endpoint for health status. Typically, the interval is slightly less than half the allocated timeout period (in seconds) which allows two full probes before taking the instance out of rotation. The default value is 15, the minimum value is 5" + default = 15 + validation { + condition = ( + var.health_check_interval > 4 + ) + error_message = "Input health_check_interval must be a number 5 or greater." + } +} + +variable "probe_threshold" { + type = number + description = "The number of consecutive successful or failed probes in order to allow or deny traffic from being delivered to this endpoint. After failing the number of consecutive probes equal to this value, the endpoint will be taken out of rotation and require the same number of successful consecutive probes to be placed back in rotation." + default = 2 +} + +variable "number_of_probes" { + type = number + description = "The number of probes where if no response, will result in stopping further traffic from being delivered to the endpoint. This values allows endpoints to be taken out of rotation faster or slower than the typical times used in Azure" + default = 1 +} diff --git a/modules/terraform-zscc-lb-azure/versions.tf b/modules/terraform-zscc-lb-azure/versions.tf index 0dc1d32..903cd9b 100755 --- a/modules/terraform-zscc-lb-azure/versions.tf +++ b/modules/terraform-zscc-lb-azure/versions.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "~> 3.46.0" + version = ">= 3.46, <= 3.74" } } required_version = ">= 0.13.7, < 2.0.0" diff --git a/modules/terraform-zscc-network-azure/README.md b/modules/terraform-zscc-network-azure/README.md index 4f5218b..ecae9bb 100644 --- a/modules/terraform-zscc-network-azure/README.md +++ b/modules/terraform-zscc-network-azure/README.md @@ -2,19 +2,43 @@ This module has multi-purpose use and is leveraged by all other Zscaler Cloud Connector child modules in some capacity. All network infrastructure resources pertaining to connectivity dependencies for a successful Cloud Connector deployment in a private subnet are referenced here. Full list of resources can be found below, but in general this module will handle all Resource Group, VNet, Subnets, NAT Gateways, Public IP, and Route Table creations to build out a resilient Azure network architecture. Most resources also have "conditional create" capabilities where, by default, they will all be created unless instructed not to with various "byo" and "enabled" variables. Use cases are documented in more detail in each description in variables.tf as well as the terraform.tfvars example file for all non-base deployment types (ie: cc_lb, etc.). +## Private DNS Resolver Network Restrictions + +
+ +### Virtual network restrictions + +The following restrictions hold with respect to virtual networks: + +- A DNS resolver can only reference a virtual network in the same region as the DNS resolver. +- A virtual network can't be shared between multiple DNS resolvers. A single virtual network can only be referenced by a single DNS resolver. +
+ +### Subnet restrictions + +Subnets used for DNS resolver have the following limitations: + +- The following IP address space is reserved and can't be used for the DNS resolver service: 10.0.1.0 - 10.0.16.255. +- Do not use these class C networks or subnets within these networks for DNS resolver subnets: 10.0.1.0/24, 10.0.2.0/24, 10.0.3.0/24, 10.0.4.0/24, 10.0.5.0/24, 10.0.6.0/24, 10.0.7.0/24, 10.0.8.0/24, 10.0.9.0/24, 10.0.10.0/24, 10.0.11.0/24, 10.0.12.0/24, 10.0.13.0/24, 10.0.14.0/24, 10.0.15.0/24, 10.0.16.0/24. +- A subnet must be a minimum of /28 address space or a maximum of /24 address space. +- A subnet can't be shared between multiple DNS resolver endpoints. A single subnet can only be used by a single DNS resolver endpoint. +- All IP configurations for a DNS resolver inbound endpoint must reference the same subnet. Spanning multiple subnets in the IP configuration for a single DNS resolver inbound endpoint isn't allowed. +- The subnet used for a DNS resolver inbound endpoint must be within the virtual network referenced by the parent DNS resolver. +
+
## Requirements | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 0.13.7, < 2.0.0 | -| [azurerm](#requirement\_azurerm) | ~> 3.46.0 | +| [azurerm](#requirement\_azurerm) | >= 3.46, <= 3.74 | ## Providers | Name | Version | |------|---------| -| [azurerm](#provider\_azurerm) | ~> 3.46.0 | +| [azurerm](#provider\_azurerm) | >= 3.46, <= 3.74 | ## Modules @@ -28,11 +52,14 @@ No modules. | [azurerm_nat_gateway_public_ip_association.ngw_association](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/nat_gateway_public_ip_association) | resource | | [azurerm_public_ip.pip](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/public_ip) | resource | | [azurerm_resource_group.rg](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group) | resource | +| [azurerm_route_table.private_dns_rt](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/route_table) | resource | | [azurerm_route_table.workload_rt](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/route_table) | resource | | [azurerm_subnet.bastion_subnet](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet) | resource | | [azurerm_subnet.cc_subnet](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet) | resource | +| [azurerm_subnet.private_dns_subnet](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet) | resource | | [azurerm_subnet.workload_subnet](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet) | resource | | [azurerm_subnet_nat_gateway_association.cc_subnet_nat_association](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet_nat_gateway_association) | resource | +| [azurerm_subnet_route_table_association.private_dns_rt_association](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet_route_table_association) | resource | | [azurerm_subnet_route_table_association.workload_rt_association](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/subnet_route_table_association) | resource | | [azurerm_virtual_network.vnet](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/virtual_network) | resource | | [azurerm_nat_gateway.ngw_selected](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/data-sources/nat_gateway) | data source | @@ -70,12 +97,14 @@ No modules. | [location](#input\_location) | Cloud Connector Azure Region | `string` | n/a | yes | | [name\_prefix](#input\_name\_prefix) | A prefix to associate to all the network module resources | `string` | `null` | no | | [network\_address\_space](#input\_network\_address\_space) | VNet IP CIDR Range. All subnet resources that might get created (public, workload, cloud connector) are derived from this /16 CIDR. If you require creating a VNet smaller than /16, you may need to explicitly define all other subnets via public\_subnets, workload\_subnets, cc\_subnets, and route53\_subnets variables | `string` | `"10.1.0.0/16"` | no | +| [private\_dns\_subnet](#input\_private\_dns\_subnet) | Private DNS Resolver Outbound Endpoint Subnet to create in VNet. This is only required if you want to override the default subnet that this code creates via network\_address\_space variable. | `string` | `null` | no | | [public\_subnets](#input\_public\_subnets) | Public/Bastion Subnets to create in VNet. This is only required if you want to override the default subnets that this code creates via network\_address\_space variable. | `list(string)` | `null` | no | | [resource\_tag](#input\_resource\_tag) | A tag to associate to all the network module resources | `string` | `null` | no | | [workloads\_enabled](#input\_workloads\_enabled) | Configure Workload Subnets, Route Tables, and associations if set to true | `bool` | `false` | no | | [workloads\_subnets](#input\_workloads\_subnets) | Workload Subnets to create in VNet. This is only required if you want to override the default subnets that this code creates via network\_address\_space variable. | `list(string)` | `null` | no | | [zones](#input\_zones) | Specify which availability zone(s) to deploy VM resources in if zones\_enabled variable is set to true | `list(string)` |
[
"1"
]
| no | | [zones\_enabled](#input\_zones\_enabled) | Determine whether to provision Cloud Connector VMs explicitly in defined zones (if supported by the Azure region provided in the location variable). If left false, Azure will automatically choose a zone and module will create an availability set resource instead for VM fault tolerance | `bool` | `false` | no | +| [zpa\_enabled](#input\_zpa\_enabled) | Configure Private DNS Resolver Outbound Endpoint Subnet and route table for conditional forwarding to Cloud Connector if set to true | `bool` | `false` | no | ## Outputs @@ -83,7 +112,9 @@ No modules. |------|-------------| | [bastion\_subnet\_ids](#output\_bastion\_subnet\_ids) | Bastion Host Subnet ID | | [cc\_subnet\_ids](#output\_cc\_subnet\_ids) | Cloud Connector Subnet ID | +| [private\_dns\_subnet\_id](#output\_private\_dns\_subnet\_id) | Private DNS Outbound Endpoint Subnet ID | | [public\_ip\_address](#output\_public\_ip\_address) | Azure Public IP Address | | [resource\_group\_name](#output\_resource\_group\_name) | Azure Resource Group Name | +| [virtual\_network\_id](#output\_virtual\_network\_id) | Azure Virtual Network ID | | [workload\_subnet\_ids](#output\_workload\_subnet\_ids) | Workloads Subnet ID | diff --git a/modules/terraform-zscc-network-azure/main.tf b/modules/terraform-zscc-network-azure/main.tf index e3105a4..2b0bb94 100755 --- a/modules/terraform-zscc-network-azure/main.tf +++ b/modules/terraform-zscc-network-azure/main.tf @@ -16,7 +16,8 @@ resource "azurerm_resource_group" "rg" { } data "azurerm_resource_group" "rg_selected" { - name = var.byo_rg == false ? azurerm_resource_group.rg[0].name : var.byo_rg_name + count = var.byo_rg ? 1 : 0 + name = var.byo_rg_name } @@ -29,14 +30,15 @@ resource "azurerm_virtual_network" "vnet" { name = "${var.name_prefix}-vnet-${var.resource_tag}" address_space = [var.network_address_space] location = var.location - resource_group_name = data.azurerm_resource_group.rg_selected.name + resource_group_name = try(data.azurerm_resource_group.rg_selected[0].name, azurerm_resource_group.rg[0].name) tags = var.global_tags } data "azurerm_virtual_network" "vnet_selected" { - name = var.byo_vnet == false ? azurerm_virtual_network.vnet[0].name : var.byo_vnet_name - resource_group_name = var.byo_vnet == false ? azurerm_virtual_network.vnet[0].resource_group_name : var.byo_vnet_subnets_rg_name + count = var.byo_vnet ? 1 : 0 + name = var.byo_vnet_name + resource_group_name = var.byo_vnet_subnets_rg_name } @@ -48,7 +50,7 @@ resource "azurerm_public_ip" "pip" { count = var.byo_pips == false && var.base_only == false ? length(distinct(var.zones)) : 0 name = "${var.name_prefix}-public-ip-${count.index + 1}-${var.resource_tag}" location = var.location - resource_group_name = data.azurerm_resource_group.rg_selected.name + resource_group_name = try(data.azurerm_resource_group.rg_selected[0].name, azurerm_resource_group.rg[0].name) allocation_method = "Static" sku = "Standard" idle_timeout_in_minutes = 30 @@ -62,9 +64,9 @@ resource "azurerm_public_ip" "pip" { } data "azurerm_public_ip" "pip_selected" { - count = var.byo_pips == false ? length(azurerm_public_ip.pip[*].id) : length(var.byo_pip_names) - name = var.byo_pips == false ? azurerm_public_ip.pip[count.index].name : element(var.byo_pip_names, count.index) - resource_group_name = var.byo_pips == false ? data.azurerm_resource_group.rg_selected.name : var.byo_pip_rg + count = var.byo_pips ? length(var.byo_pip_names) : 0 + name = element(var.byo_pip_names, count.index) + resource_group_name = var.byo_pip_rg } @@ -73,7 +75,7 @@ resource "azurerm_nat_gateway" "ngw" { count = var.byo_nat_gws == false && var.base_only == false ? length(distinct(var.zones)) : 0 name = "${var.name_prefix}-ngw-${count.index + 1}-${var.resource_tag}" location = var.location - resource_group_name = data.azurerm_resource_group.rg_selected.name + resource_group_name = try(data.azurerm_resource_group.rg_selected[0].name, azurerm_resource_group.rg[0].name) idle_timeout_in_minutes = 10 zones = local.zones_supported ? [element(var.zones, count.index)] : null @@ -81,21 +83,20 @@ resource "azurerm_nat_gateway" "ngw" { } data "azurerm_nat_gateway" "ngw_selected" { - count = var.byo_nat_gws == false ? length(azurerm_nat_gateway.ngw[*].id) : length(var.byo_nat_gw_names) - name = var.byo_nat_gws == false ? azurerm_nat_gateway.ngw[count.index].name : element(var.byo_nat_gw_names, count.index) - resource_group_name = var.byo_nat_gws == false ? data.azurerm_resource_group.rg_selected.name : var.byo_nat_gw_rg + count = var.byo_nat_gws ? length(var.byo_nat_gw_names) : 0 + name = element(var.byo_nat_gw_names, count.index) + resource_group_name = var.byo_nat_gw_rg } # Associate Public IP to NAT Gateway -resource "azurerm_nat_gateway_public_ip_association" "ngw_association" { - count = var.existing_nat_gw_pip_association == false ? length(data.azurerm_nat_gateway.ngw_selected[*].id) : 0 - nat_gateway_id = data.azurerm_nat_gateway.ngw_selected[count.index].id - public_ip_address_id = data.azurerm_public_ip.pip_selected[count.index].id +locals { + ngw_associations_selected = var.byo_nat_gws ? data.azurerm_nat_gateway.ngw_selected[*].id : azurerm_nat_gateway.ngw[*].id +} - depends_on = [ - data.azurerm_public_ip.pip_selected, - data.azurerm_nat_gateway.ngw_selected - ] +resource "azurerm_nat_gateway_public_ip_association" "ngw_association" { + count = var.existing_nat_gw_pip_association ? 0 : length(local.ngw_associations_selected) + nat_gateway_id = try(data.azurerm_nat_gateway.ngw_selected[count.index].id, azurerm_nat_gateway.ngw[count.index].id) + public_ip_address_id = try(data.azurerm_public_ip.pip_selected[count.index].id, azurerm_public_ip.pip[count.index].id) } @@ -106,8 +107,8 @@ resource "azurerm_nat_gateway_public_ip_association" "ngw_association" { resource "azurerm_subnet" "cc_subnet" { count = var.byo_subnets == false && var.base_only == false ? length(distinct(var.zones)) : 0 name = "${var.name_prefix}-cc-subnet-${count.index + 1}-${var.resource_tag}" - resource_group_name = var.byo_vnet == false ? data.azurerm_virtual_network.vnet_selected.resource_group_name : var.byo_vnet_subnets_rg_name - virtual_network_name = var.byo_vnet == false ? data.azurerm_virtual_network.vnet_selected.name : var.byo_vnet_name + resource_group_name = var.byo_vnet == false ? try(data.azurerm_virtual_network.vnet_selected[0].resource_group_name, azurerm_virtual_network.vnet[0].resource_group_name) : var.byo_vnet_subnets_rg_name + virtual_network_name = var.byo_vnet == false ? try(data.azurerm_virtual_network.vnet_selected[0].name, azurerm_virtual_network.vnet[0].name) : var.byo_vnet_name address_prefixes = var.cc_subnets != null ? [element(var.cc_subnets, count.index)] : [cidrsubnet(var.network_address_space, 8, count.index + 200)] } @@ -115,19 +116,18 @@ resource "azurerm_subnet" "cc_subnet" { data "azurerm_subnet" "cc_subnet_selected" { count = var.byo_subnets == false ? length(azurerm_subnet.cc_subnet[*].id) : length(var.byo_subnet_names) name = var.byo_subnets == false ? azurerm_subnet.cc_subnet[count.index].name : element(var.byo_subnet_names, count.index) - resource_group_name = var.byo_vnet == false ? data.azurerm_virtual_network.vnet_selected.resource_group_name : var.byo_vnet_subnets_rg_name - virtual_network_name = var.byo_vnet == false ? data.azurerm_virtual_network.vnet_selected.name : var.byo_vnet_name + resource_group_name = var.byo_vnet == false ? try(data.azurerm_virtual_network.vnet_selected[0].resource_group_name, azurerm_virtual_network.vnet[0].resource_group_name) : var.byo_vnet_subnets_rg_name + virtual_network_name = var.byo_vnet == false ? try(data.azurerm_virtual_network.vnet_selected[0].name, azurerm_virtual_network.vnet[0].name) : var.byo_vnet_name } # Associate Cloud Connector Subnet to NAT Gateway resource "azurerm_subnet_nat_gateway_association" "cc_subnet_nat_association" { count = var.existing_nat_gw_subnet_association == false ? length(data.azurerm_subnet.cc_subnet_selected[*].id) : 0 subnet_id = data.azurerm_subnet.cc_subnet_selected[count.index].id - nat_gateway_id = data.azurerm_nat_gateway.ngw_selected[count.index].id + nat_gateway_id = try(data.azurerm_nat_gateway.ngw_selected[count.index].id, azurerm_nat_gateway.ngw[count.index].id) depends_on = [ data.azurerm_subnet.cc_subnet_selected, - data.azurerm_nat_gateway.ngw_selected ] } @@ -139,8 +139,8 @@ resource "azurerm_subnet_nat_gateway_association" "cc_subnet_nat_association" { resource "azurerm_subnet" "workload_subnet" { count = var.workloads_enabled == true ? 1 : 0 name = "${var.name_prefix}-workload-subnet-${var.resource_tag}" - resource_group_name = data.azurerm_resource_group.rg_selected.name - virtual_network_name = data.azurerm_virtual_network.vnet_selected.name + resource_group_name = try(data.azurerm_resource_group.rg_selected[0].name, azurerm_resource_group.rg[0].name) + virtual_network_name = try(data.azurerm_virtual_network.vnet_selected[0].name, azurerm_virtual_network.vnet[0].name) address_prefixes = var.workloads_subnets != null ? [element(var.workloads_subnets, count.index)] : [cidrsubnet(var.network_address_space, 8, count.index + 1)] } @@ -149,7 +149,7 @@ resource "azurerm_route_table" "workload_rt" { count = var.workloads_enabled == true ? 1 : 0 name = "${var.name_prefix}-workload-rt-${var.resource_tag}" location = var.location - resource_group_name = data.azurerm_resource_group.rg_selected.name + resource_group_name = try(data.azurerm_resource_group.rg_selected[0].name, azurerm_resource_group.rg[0].name) disable_bgp_route_propagation = true @@ -176,7 +176,52 @@ resource "azurerm_subnet_route_table_association" "workload_rt_association" { resource "azurerm_subnet" "bastion_subnet" { count = var.bastion_enabled == true ? 1 : 0 name = "${var.name_prefix}-bastion-subnet-${var.resource_tag}" - resource_group_name = data.azurerm_resource_group.rg_selected.name - virtual_network_name = data.azurerm_virtual_network.vnet_selected.name + resource_group_name = try(data.azurerm_resource_group.rg_selected[0].name, azurerm_resource_group.rg[0].name) + virtual_network_name = try(data.azurerm_virtual_network.vnet_selected[0].name, azurerm_virtual_network.vnet[0].name) address_prefixes = var.public_subnets != null ? [element(var.public_subnets, count.index)] : [cidrsubnet(var.network_address_space, 8, 101)] } + + +################################################################################ +# Outbound Private DNS Subnet and Route Table +################################################################################ +# Create private subnet for outbound private DNS and delegate to dnsResolvers service +resource "azurerm_subnet" "private_dns_subnet" { + count = var.zpa_enabled ? 1 : 0 + name = "${var.name_prefix}-outbound-dns-subnet-${var.resource_tag}" + resource_group_name = try(data.azurerm_resource_group.rg_selected[0].name, azurerm_resource_group.rg[0].name) + virtual_network_name = try(data.azurerm_virtual_network.vnet_selected[0].name, azurerm_virtual_network.vnet[0].name) + address_prefixes = var.private_dns_subnet != null ? [var.private_dns_subnet] : [cidrsubnet(var.network_address_space, 12, 2480)] + + delegation { + name = "Microsoft.Network.dnsResolvers" + service_delegation { + actions = ["Microsoft.Network/virtualNetworks/subnets/join/action"] + name = "Microsoft.Network/dnsResolvers" + } + } +} + +# Create Outbound DNS Route Table to send to Cloud Connector +resource "azurerm_route_table" "private_dns_rt" { + count = var.zpa_enabled ? 1 : 0 + name = "${var.name_prefix}-outbound-dns-rt-${var.resource_tag}" + location = var.location + resource_group_name = try(data.azurerm_resource_group.rg_selected[0].name, azurerm_resource_group.rg[0].name) + + disable_bgp_route_propagation = true + + route { + name = "default-route" + address_prefix = "0.0.0.0/0" + next_hop_type = "VirtualAppliance" + next_hop_in_ip_address = var.lb_enabled == true ? var.lb_frontend_ip : element(var.cc_service_ip, count.index) + } +} + +# Associate Route Table with Outbound DNS Subnet +resource "azurerm_subnet_route_table_association" "private_dns_rt_association" { + count = length(azurerm_route_table.private_dns_rt[*].id) + subnet_id = azurerm_subnet.private_dns_subnet[count.index].id + route_table_id = azurerm_route_table.private_dns_rt[count.index].id +} diff --git a/modules/terraform-zscc-network-azure/outputs.tf b/modules/terraform-zscc-network-azure/outputs.tf index 6f309e7..59d7cde 100755 --- a/modules/terraform-zscc-network-azure/outputs.tf +++ b/modules/terraform-zscc-network-azure/outputs.tf @@ -1,6 +1,6 @@ output "resource_group_name" { description = "Azure Resource Group Name" - value = data.azurerm_resource_group.rg_selected.name + value = var.byo_rg ? data.azurerm_resource_group.rg_selected[0].name : azurerm_resource_group.rg[0].name } output "cc_subnet_ids" { @@ -10,7 +10,7 @@ output "cc_subnet_ids" { output "public_ip_address" { description = "Azure Public IP Address" - value = data.azurerm_public_ip.pip_selected[*].ip_address + value = var.byo_pips ? data.azurerm_public_ip.pip_selected[*].ip_address : azurerm_public_ip.pip[*].ip_address } output "bastion_subnet_ids" { @@ -22,3 +22,13 @@ output "workload_subnet_ids" { description = "Workloads Subnet ID" value = azurerm_subnet.workload_subnet[*].id } + +output "virtual_network_id" { + description = "Azure Virtual Network ID" + value = var.byo_vnet ? data.azurerm_virtual_network.vnet_selected[0].id : azurerm_virtual_network.vnet[0].id +} + +output "private_dns_subnet_id" { + description = "Private DNS Outbound Endpoint Subnet ID" + value = var.zpa_enabled ? azurerm_subnet.private_dns_subnet[0].id : "" +} diff --git a/modules/terraform-zscc-network-azure/variables.tf b/modules/terraform-zscc-network-azure/variables.tf index 7f97866..48e2f37 100755 --- a/modules/terraform-zscc-network-azure/variables.tf +++ b/modules/terraform-zscc-network-azure/variables.tf @@ -45,6 +45,12 @@ variable "public_subnets" { default = null } +variable "private_dns_subnet" { + type = string + description = "Private DNS Resolver Outbound Endpoint Subnet to create in VNet. This is only required if you want to override the default subnet that this code creates via network_address_space variable." + default = null +} + # Validation to determine if Azure Region selected supports availabilty zones if desired locals { az_supported_regions = ["australiaeast", "Australia East", "brazilsouth", "Brazil South", "canadacentral", "Canada Central", "centralindia", "Central India", "centralus", "Central US", "eastasia", "East Asia", "eastus", "East US", "francecentral", "France Central", "germanywestcentral", "Germany West Central", "japaneast", "Japan East", "koreacentral", "Korea Central", "northeurope", "North Europe", "norwayeast", "Norway East", "southafricanorth", "South Africa North", "southcentralus", "South Central US", "southeastasia", "Southeast Asia", "swedencentral", "Sweden Central", "uksouth", "UK South", "westeurope", "West Europe", "westus2", "West US 2", "westus3", "West US 3"] @@ -107,6 +113,12 @@ variable "cc_service_ip" { default = [""] } +variable "zpa_enabled" { + type = bool + default = false + description = "Configure Private DNS Resolver Outbound Endpoint Subnet and route table for conditional forwarding to Cloud Connector if set to true" +} + # BYO (Bring-your-own) variables list variable "byo_rg" { diff --git a/modules/terraform-zscc-network-azure/versions.tf b/modules/terraform-zscc-network-azure/versions.tf index 24849f6..192026f 100755 --- a/modules/terraform-zscc-network-azure/versions.tf +++ b/modules/terraform-zscc-network-azure/versions.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "~> 3.46.0" + version = ">= 3.46, <= 3.74" } local = { source = "hashicorp/local" diff --git a/modules/terraform-zscc-nsg-azure/README.md b/modules/terraform-zscc-nsg-azure/README.md index 29f7ae8..46f8148 100644 --- a/modules/terraform-zscc-nsg-azure/README.md +++ b/modules/terraform-zscc-nsg-azure/README.md @@ -8,13 +8,13 @@ This module can be used to create default Management and Service interface NSG r | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 0.13.7, < 2.0.0 | -| [azurerm](#requirement\_azurerm) | ~> 3.46.0 | +| [azurerm](#requirement\_azurerm) | >= 3.46, <= 3.74 | ## Providers | Name | Version | |------|---------| -| [azurerm](#provider\_azurerm) | ~> 3.46.0 | +| [azurerm](#provider\_azurerm) | >= 3.46, <= 3.74 | ## Modules diff --git a/modules/terraform-zscc-nsg-azure/versions.tf b/modules/terraform-zscc-nsg-azure/versions.tf index 0dc1d32..903cd9b 100755 --- a/modules/terraform-zscc-nsg-azure/versions.tf +++ b/modules/terraform-zscc-nsg-azure/versions.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "~> 3.46.0" + version = ">= 3.46, <= 3.74" } } required_version = ">= 0.13.7, < 2.0.0" diff --git a/modules/terraform-zscc-private-dns-azure/README.md b/modules/terraform-zscc-private-dns-azure/README.md new file mode 100755 index 0000000..fbdb036 --- /dev/null +++ b/modules/terraform-zscc-private-dns-azure/README.md @@ -0,0 +1,104 @@ +# Zscaler Cloud Connector / Azure DNS Private Resolver Module + +This module creates the required resource dependencies to deploy Azure Private DNS that can be utilized to facilitate conditional DNS forwarding from Azure to Zscaler Cloud Connector for ZPA resolution and/or ZIA DNS Controls security. Resources included: Private DNS Resolver, Ruleset, Forwarding Rules, and Outbound Endpoint resources. For more information about Azure DNS Private Resolver, check here: https://learn.microsoft.com/en-us/azure/dns/dns-private-resolver-overview + +See: [Private Resolver Architecture](https://learn.microsoft.com/en-us/azure/dns/private-resolver-architecture) for additional information discussing two architectural design options that are available to resolve DNS names, including private DNS zones across your Azure network using an Azure DNS Private Resolver. +See: [Private Resolver Resiliency/Reliability](https://learn.microsoft.com/en-us/azure/dns/private-resolver-reliability) for additional information describing reliability support in Azure DNS Private Resolver. + + + +## Considerations + +If deploying via one of the Zscaler provider example templates, there is a creation dependency with the terraform-zscc-network-azure module where Terraform will create a new subnet delegated for the dnsResolvers service in the same VNet as the Cloud Connector(s). This subnet is where the Outbound Endpoint gets created. Terraform will also create a new Route Table associated with this subnet pointing a default route (0.0.0.0/0) towards the Cloud Connector service IP or Load Balancer Front-End IP for HA deployments to ensure that conditionally forwarded request get redirected to the Cloud Connector. + +This module can create multiple Resolver Rules and links associated with only a single VNet. This follows Azure Well-Architected Framework and Hub-Spoke design model where spokes have access/connectivity directly to the Hub Virtual Network. + +Rule domain names must be dot-terminated and have either zero labels (for wildcard) or between 2 and 34 labels). For example, contoso.com. has two labels. + +## Azure Limitations + +https://learn.microsoft.com/en-us/azure/dns/dns-faq#what-are-the-usage-limits-for-azure-dns-:~:text=limits%20are%20dropped.-,DNS%20private%20resolver,-Resource + +Rulesets have the following associations: + +- A single ruleset can be associated with multiple outbound endpoints. +- A ruleset can have up to 25 DNS forwarding rules. +- A ruleset can be linked to up to 10 virtual networks in the same region + +## Private DNS Resolver Network Restrictions + +
+ +### Virtual network restrictions + +The following restrictions hold with respect to virtual networks: + +- A DNS resolver can only reference a virtual network in the same region as the DNS resolver. +- A virtual network can't be shared between multiple DNS resolvers. A single virtual network can only be referenced by a single DNS resolver. +
+ +### Subnet restrictions + +Subnets used for DNS resolver have the following limitations: + +- The following IP address space is reserved and can't be used for the DNS resolver service: 10.0.1.0 - 10.0.16.255. +- Do not use these class C networks or subnets within these networks for DNS resolver subnets: 10.0.1.0/24, 10.0.2.0/24, 10.0.3.0/24, 10.0.4.0/24, 10.0.5.0/24, 10.0.6.0/24, 10.0.7.0/24, 10.0.8.0/24, 10.0.9.0/24, 10.0.10.0/24, 10.0.11.0/24, 10.0.12.0/24, 10.0.13.0/24, 10.0.14.0/24, 10.0.15.0/24, 10.0.16.0/24. +- A subnet must be a minimum of /28 address space or a maximum of /24 address space. +- A subnet can't be shared between multiple DNS resolver endpoints. A single subnet can only be used by a single DNS resolver endpoint. +- All IP configurations for a DNS resolver inbound endpoint must reference the same subnet. Spanning multiple subnets in the IP configuration for a single DNS resolver inbound endpoint isn't allowed. +- The subnet used for a DNS resolver inbound endpoint must be within the virtual network referenced by the parent DNS resolver. +
+
+ + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | >= 0.13.7, < 2.0.0 | +| [azurerm](#requirement\_azurerm) | >= 3.46, <= 3.74 | + +## Providers + +| Name | Version | +|------|---------| +| [azurerm](#provider\_azurerm) | >= 3.46, <= 3.74 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [azurerm_private_dns_resolver.dns_resolver](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_resolver) | resource | +| [azurerm_private_dns_resolver_dns_forwarding_ruleset.dns_forwarding_ruleset](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_resolver_dns_forwarding_ruleset) | resource | +| [azurerm_private_dns_resolver_forwarding_rule.dns_forwarding_rule](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_resolver_forwarding_rule) | resource | +| [azurerm_private_dns_resolver_outbound_endpoint.dns_outbound_endpoint](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/private_dns_resolver_outbound_endpoint) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [domain\_names](#input\_domain\_names) | Domain names fqdn/wildcard to have Azure Private DNS redirect DNS requests to Cloud Connector | `map(any)` | n/a | yes | +| [global\_tags](#input\_global\_tags) | Populate any custom user defined tags from a map | `map(string)` | `{}` | no | +| [location](#input\_location) | Specifies the Azure Region where the Private DNS Resolver should exist | `string` | n/a | yes | +| [name\_prefix](#input\_name\_prefix) | A prefix to associate to all the Private DNS module resources | `string` | `null` | no | +| [private\_dns\_subnet\_id](#input\_private\_dns\_subnet\_id) | The ID of the Subnet that is linked to the Private DNS Resolver Outbound Endpoint | `string` | n/a | yes | +| [resource\_group](#input\_resource\_group) | Specifies the name of the Resource Group where the Private DNS Resolver should exist | `string` | n/a | yes | +| [resource\_tag](#input\_resource\_tag) | A tag to associate to all the Private DNS module resources | `string` | `null` | no | +| [target\_address](#input\_target\_address) | Azure DNS queries will be conditionally forwarded to these target IP addresses. Default are a pair of Zscaler Global VIP addresses | `list(string)` |
[
"185.46.212.88",
"185.46.212.89"
]
| no | +| [vnet\_id](#input\_vnet\_id) | The ID of the Virtual Network that is linked to the Private DNS Resolver | `string` | n/a | yes | + +## Outputs + +| Name | Description | +|------|-------------| +| [private\_dns\_forwarding\_ruleset\_id](#output\_private\_dns\_forwarding\_ruleset\_id) | The ID of the Private DNS Resolver Dns Forwarding Ruleset. Use this if you want to link other Virtual Networks to this Ruleset | +| [private\_dns\_forwarding\_ruleset\_name](#output\_private\_dns\_forwarding\_ruleset\_name) | The name of the Private DNS Resolver Dns Forwarding Ruleset. Use this if you want to link other Virtual Networks to this Ruleset | +| [private\_dns\_outbound\_endpoint\_id](#output\_private\_dns\_outbound\_endpoint\_id) | The ID of the Private DNS Resolver Outbound Endpoint | +| [private\_dns\_outbound\_endpoint\_name](#output\_private\_dns\_outbound\_endpoint\_name) | The name of the Private DNS Resolver Outbound Endpoint | +| [private\_dns\_resolver\_id](#output\_private\_dns\_resolver\_id) | The ID of the Private DNS Resolver | +| [private\_dns\_resolver\_name](#output\_private\_dns\_resolver\_name) | The name of the Private DNS Resolver | + diff --git a/modules/terraform-zscc-private-dns-azure/main.tf b/modules/terraform-zscc-private-dns-azure/main.tf new file mode 100755 index 0000000..d7edc5d --- /dev/null +++ b/modules/terraform-zscc-private-dns-azure/main.tf @@ -0,0 +1,58 @@ +################################################################################ +# Create Azure Private DNS Resolver +################################################################################ +resource "azurerm_private_dns_resolver" "dns_resolver" { + name = "${var.name_prefix}-${var.location}-dns-resolver-${var.resource_tag}" + resource_group_name = var.resource_group + location = var.location + virtual_network_id = var.vnet_id + + tags = var.global_tags +} + + +################################################################################ +# Create Azure Private DNS Resolver Outbound Endpoint +################################################################################ +resource "azurerm_private_dns_resolver_outbound_endpoint" "dns_outbound_endpoint" { + name = "${var.name_prefix}-outbound-endpoint-${var.resource_tag}" + private_dns_resolver_id = azurerm_private_dns_resolver.dns_resolver.id + location = var.location + subnet_id = var.private_dns_subnet_id + + tags = var.global_tags +} + + +################################################################################ +# Create Azure Private DNS Resolver Forwarding Ruleset +################################################################################ +resource "azurerm_private_dns_resolver_dns_forwarding_ruleset" "dns_forwarding_ruleset" { + name = "${var.name_prefix}-fwd-ruleset-${var.resource_tag}" + resource_group_name = var.resource_group + location = var.location + private_dns_resolver_outbound_endpoint_ids = [azurerm_private_dns_resolver_outbound_endpoint.dns_outbound_endpoint.id] + + tags = var.global_tags +} + + +################################################################################ +# Create Azure Private DNS Resolver Forwarding Rules +################################################################################ +resource "azurerm_private_dns_resolver_forwarding_rule" "dns_forwarding_rule" { + for_each = var.domain_names + name = "${var.name_prefix}-fwd-rule-${each.key}-${var.resource_tag}" + dns_forwarding_ruleset_id = azurerm_private_dns_resolver_dns_forwarding_ruleset.dns_forwarding_ruleset.id + domain_name = each.value + enabled = true + + dynamic "target_dns_servers" { + for_each = var.target_address + + content { + ip_address = target_dns_servers.value + port = 53 + } + } +} diff --git a/modules/terraform-zscc-private-dns-azure/outputs.tf b/modules/terraform-zscc-private-dns-azure/outputs.tf new file mode 100755 index 0000000..1f8afcd --- /dev/null +++ b/modules/terraform-zscc-private-dns-azure/outputs.tf @@ -0,0 +1,29 @@ +output "private_dns_resolver_id" { + description = "The ID of the Private DNS Resolver" + value = azurerm_private_dns_resolver.dns_resolver.id +} + +output "private_dns_resolver_name" { + description = "The name of the Private DNS Resolver" + value = azurerm_private_dns_resolver.dns_resolver.name +} + +output "private_dns_outbound_endpoint_id" { + description = "The ID of the Private DNS Resolver Outbound Endpoint" + value = azurerm_private_dns_resolver_outbound_endpoint.dns_outbound_endpoint.id +} + +output "private_dns_outbound_endpoint_name" { + description = "The name of the Private DNS Resolver Outbound Endpoint" + value = azurerm_private_dns_resolver_outbound_endpoint.dns_outbound_endpoint.name +} + +output "private_dns_forwarding_ruleset_id" { + description = "The ID of the Private DNS Resolver Dns Forwarding Ruleset. Use this if you want to link other Virtual Networks to this Ruleset" + value = azurerm_private_dns_resolver_dns_forwarding_ruleset.dns_forwarding_ruleset.id +} + +output "private_dns_forwarding_ruleset_name" { + description = "The name of the Private DNS Resolver Dns Forwarding Ruleset. Use this if you want to link other Virtual Networks to this Ruleset" + value = azurerm_private_dns_resolver_dns_forwarding_ruleset.dns_forwarding_ruleset.name +} diff --git a/modules/terraform-zscc-private-dns-azure/variables.tf b/modules/terraform-zscc-private-dns-azure/variables.tf new file mode 100755 index 0000000..ae43f29 --- /dev/null +++ b/modules/terraform-zscc-private-dns-azure/variables.tf @@ -0,0 +1,48 @@ +variable "name_prefix" { + type = string + description = "A prefix to associate to all the Private DNS module resources" + default = null +} + +variable "resource_tag" { + type = string + description = "A tag to associate to all the Private DNS module resources" + default = null +} + +variable "global_tags" { + type = map(string) + description = "Populate any custom user defined tags from a map" + default = {} +} + +variable "resource_group" { + type = string + description = "Specifies the name of the Resource Group where the Private DNS Resolver should exist" +} + +variable "location" { + type = string + description = "Specifies the Azure Region where the Private DNS Resolver should exist" +} + +variable "vnet_id" { + type = string + description = "The ID of the Virtual Network that is linked to the Private DNS Resolver" +} + +variable "private_dns_subnet_id" { + type = string + description = "The ID of the Subnet that is linked to the Private DNS Resolver Outbound Endpoint" +} + +variable "target_address" { + type = list(string) + description = "Azure DNS queries will be conditionally forwarded to these target IP addresses. Default are a pair of Zscaler Global VIP addresses" + default = ["185.46.212.88", "185.46.212.89"] +} + +variable "domain_names" { + type = map(any) + description = "Domain names fqdn/wildcard to have Azure Private DNS redirect DNS requests to Cloud Connector" +} diff --git a/modules/terraform-zscc-private-dns-azure/versions.tf b/modules/terraform-zscc-private-dns-azure/versions.tf new file mode 100755 index 0000000..903cd9b --- /dev/null +++ b/modules/terraform-zscc-private-dns-azure/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">= 3.46, <= 3.74" + } + } + required_version = ">= 0.13.7, < 2.0.0" +} diff --git a/modules/terraform-zscc-workload-azure/README.md b/modules/terraform-zscc-workload-azure/README.md index 6cc6951..630b68e 100644 --- a/modules/terraform-zscc-workload-azure/README.md +++ b/modules/terraform-zscc-workload-azure/README.md @@ -8,13 +8,13 @@ This module creates all Azure VM and NSG resources needed to deploy test workloa | Name | Version | |------|---------| | [terraform](#requirement\_terraform) | >= 0.13.7, < 2.0.0 | -| [azurerm](#requirement\_azurerm) | ~> 3.46.0 | +| [azurerm](#requirement\_azurerm) | >= 3.46, <= 3.74 | ## Providers | Name | Version | |------|---------| -| [azurerm](#provider\_azurerm) | ~> 3.46.0 | +| [azurerm](#provider\_azurerm) | >= 3.46, <= 3.74 | ## Modules diff --git a/modules/terraform-zscc-workload-azure/versions.tf b/modules/terraform-zscc-workload-azure/versions.tf index 0dc1d32..903cd9b 100755 --- a/modules/terraform-zscc-workload-azure/versions.tf +++ b/modules/terraform-zscc-workload-azure/versions.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "~> 3.46.0" + version = ">= 3.46, <= 3.74" } } required_version = ">= 0.13.7, < 2.0.0"