From c9fe6291a96abb119ce503defa686ffb9f3c1617 Mon Sep 17 00:00:00 2001 From: "pan-dev-content-sync-trigger[bot]" Date: Thu, 16 Apr 2026 04:02:56 +0000 Subject: [PATCH] Sync azure Terraform module documentation --- .../azure/cloudngfw/modules/loadbalancer.md | 21 +- .../azure/cloudngfw/modules/name_templater.md | 4 +- .../swfw/azure/cloudngfw/modules/vmseries.md | 109 +- .../docs/swfw/azure/cloudngfw/modules/vmss.md | 122 +- .../docs/swfw/azure/cloudngfw/modules/vwan.md | 3 + .../cloudngfw_centralized_single_vwan.md | 17 +- .../cloudngfw_centralized_vnet.md | 17 +- .../cloudngfw_distributed.md | 17 +- .../azure/vmseries/modules/loadbalancer.md | 21 +- .../swfw/azure/vmseries/modules/vmseries.md | 109 +- .../docs/swfw/azure/vmseries/modules/vmss.md | 122 +- .../docs/swfw/azure/vmseries/modules/vwan.md | 3 + .../2ef99143-3dcf-4113-814d-aedca8fcccf3.png | Bin 0 -> 77848 bytes .../vmseries_transit_vnet_common.md | 89 +- .../vmseries_transit_vnet_common_autoscale.md | 70 +- .../vmseries_transit_vnet_dedicated.md | 89 +- ...series_transit_vnet_dedicated_autoscale.md | 72 +- .../vmseries_transit_vnet_dedicated_vwan.md | 1703 +++++++++++++++++ 18 files changed, 2259 insertions(+), 329 deletions(-) create mode 100644 products/terraform/docs/swfw/azure/vmseries/reference-architectures/2ef99143-3dcf-4113-814d-aedca8fcccf3.png create mode 100644 products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_dedicated_vwan.md diff --git a/products/terraform/docs/swfw/azure/cloudngfw/modules/loadbalancer.md b/products/terraform/docs/swfw/azure/cloudngfw/modules/loadbalancer.md index a7a723d0a..d09d47ef1 100644 --- a/products/terraform/docs/swfw/azure/cloudngfw/modules/loadbalancer.md +++ b/products/terraform/docs/swfw/azure/cloudngfw/modules/loadbalancer.md @@ -111,11 +111,11 @@ module "lbe" { ### Requirements - `terraform`, version: >= 1.5, < 2.0 -- `azurerm`, version: ~> 4.0 +- `azurerm`, version: ~> 4.42 ### Providers -- `azurerm`, version: ~> 4.0 +- `azurerm`, version: ~> 4.42 @@ -344,14 +344,15 @@ map(object({ private_ip_address = optional(string) gwlb_fip_id = optional(string) in_rules = optional(map(object({ - name = string - protocol = string - port = number - backend_port = optional(number) - health_probe_key = optional(string, "default") - floating_ip = optional(bool, true) - session_persistence = optional(string, "Default") - nsg_priority = optional(number) + name = string + protocol = string + port = number + backend_port = optional(number) + health_probe_key = optional(string, "default") + floating_ip = optional(bool, true) + session_persistence = optional(string, "Default") + nsg_priority = optional(number) + idle_timeout_in_minutes = optional(number) })), {}) out_rules = optional(map(object({ name = string diff --git a/products/terraform/docs/swfw/azure/cloudngfw/modules/name_templater.md b/products/terraform/docs/swfw/azure/cloudngfw/modules/name_templater.md index 03829ea7a..c035885eb 100644 --- a/products/terraform/docs/swfw/azure/cloudngfw/modules/name_templater.md +++ b/products/terraform/docs/swfw/azure/cloudngfw/modules/name_templater.md @@ -61,7 +61,7 @@ As you can see: * the `prefix` key is just a placeholder that eventually is replaced with the value of `name_prefix` * the `__random__` string is replaced with a name of a [random pet](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/pet) (in case you need to randomize some name, for testing purposes for example) * the `__default__` string is replaced with a resource abbreviation. - This abbreviations are defined with `var.abbreviations` variable. The module contains basic abbreviations following Microsoft suggestions, but they can be overridden with custom definitions. + This abbreviations are defined with `var.abbreviations` variable. The module contains basic abbreviations following Microsoft suggestions, but they can be overriden with custom definitions. The important part is that the `resource_type` has to match an entry in `abbreviations` variable, otherwise the abbreviation will be replaced with an empty string. To create the actual resource name the following code can be used: @@ -199,4 +199,4 @@ Type: map(string) Default value: `map[application_gw:agw application_insights:appi availability_set:avail bastion:bas data_disk:disk file_share:share load_balancer:lb log_analytics_workspace:log managed_identity:id nat_gw:ng network_interface:nic nsg:nsg nsg_rule:nsgsr os_disk:osdisk public_ip:pip public_ip_prefix:ippre resource_group:rg route_table:rt service_endpoint:se storage_account:st subnet:snet udr:udr virtual_machine:vm virtual_machine_scale_set:vmss virtual_network_gateway:vgw vnet:vnet vnet_peering:peer]` -[back to list](#modules-optional-inputs) +[back to list](#modules-optional-inputs) \ No newline at end of file diff --git a/products/terraform/docs/swfw/azure/cloudngfw/modules/vmseries.md b/products/terraform/docs/swfw/azure/cloudngfw/modules/vmseries.md index ddcd4ff32..dc9d3f1a1 100644 --- a/products/terraform/docs/swfw/azure/cloudngfw/modules/vmseries.md +++ b/products/terraform/docs/swfw/azure/cloudngfw/modules/vmseries.md @@ -212,7 +212,7 @@ Firewall parameters configuration. This map contains basic, as well as some optional Firewall parameters. Both types contain sane defaults. Nevertheless they should be at least reviewed to meet deployment requirements. -List of either required or important properties: +List of either required or important properties: - `size` - (`string`, optional, defaults to `Standard_D3_v2`) Azure VM size (type). Consult the *VM-Series Deployment Guide* as only a few selected sizes are supported. @@ -231,7 +231,7 @@ List of either required or important properties: For more details on bootstrapping [see documentation](https://docs.paloaltonetworks.com/vm-series/10-2/vm-series-deployment/bootstrap-the-vm-series-firewall/create-the-init-cfgtxt-file/init-cfgtxt-file-components). -List of other, optional properties: +List of other, optional properties: - `avset_id` - (`string`, optional, default to `null`) identifier of the Availability Set to use. - `capacity_reservation_group_id` - (`string`, optional, defaults to `null`) specifies the ID of the Capacity Reservation Group @@ -250,7 +250,7 @@ List of other, optional properties: - `identity_type` - (`string`, optional, defaults to `SystemAssigned`) type of Managed Service Identity that should be configured on this VM. Can be one of "SystemAssigned", "UserAssigned" or "SystemAssigned, UserAssigned". -- `identity_ids` - (`list`, optional, defaults to `[]`) a list of User Assigned Managed Identity IDs to be +- `identity_ids` - (`list`, optional, defaults to `[]`) a list of User Assigned Managed Identity IDs to be assigned to this VM. Required only if `identity_type` is not "SystemAssigned". @@ -290,26 +290,30 @@ Interfaces will be attached to VM in the order you define here, therefore: - The first should be the management interface, which does not participate in data filtering. - The remaining ones are the dataplane interfaces. - + Following configuration options are available: - `name` - (`string`, required) the interface name. - `subnet_id` - (`string`, required) ID of an existing subnet to create the interface in. -- `ip_configuration_name` - (`string`, optional, defaults to `primary`) the name of the interface IP configuration. -- `private_ip_address` - (`string`, optional, defaults to `null`) static private IP to assign to the interface. When - skipped Azure will assign one dynamically. Keep in mind that a dynamic IP is guarantied not - to change as long as the VM is running. Any stop/deallocate/restart operation might cause - the IP to change. -- `create_public_ip` - (`bool`, optional, defaults to `false`) if `true`, creates a public IP for the interface. -- `public_ip_name` - (`string`, optional, defaults to `null`) name of the public IP to associate with the - interface. When `create_public_ip` is set to `true` this will become a name of a newly - created Public IP interface. Otherwise this is a name of an existing interfaces that will - be sourced and attached to the interface. Not used when using `public_ip` module. -- `public_ip_resource_group_name` - (`string`, optional, defaults to `var.resource_group_name`) name of a Resource Group that - contains public IP that that will be associated with the interface. Used only when - `create_public_ip` is `false`. -- `public_ip_id` - (`string`, optional, defaults to `null`) ID of the public IP to associate with the - interface. Property is used when public IP is not created or sourced within this module. +- ip_configurations - (`map`, required) A map that contains the IP configurations for the interface. + - `name` - (`string`, optional, defaults to `primary`) the name of the interface IP configuration. + - `primary` - (`bool`, optional, defaults to `true`) sets the current IP configuration as the primary + one. + - `private_ip_address` - (`string`, optional, defaults to `null`) static private IP to assign to the interface. + When skipped Azure will assign one dynamically. Keep in mind that a dynamic IP is + guaranteed not to change as long as the VM is running. Any stop/deallocate/restart + operation might cause the IP to change. + - `create_public_ip` - (`bool`, optional, defaults to `false`) if `true`, creates a public IP for the interface. + **Note!** When you define multiple IP configurations, exactly one must be the primary. + - `public_ip_name` - (`string`, optional, defaults to `null`) name of the public IP to associate with the + interface. When `create_public_ip` is set to `true` this will become a name of a newly + created Public IP interface. Otherwise this is a name of an existing interfaces that will + be sourced and attached to the interface. Not used when using `public_ip` module. + - `public_ip_resource_group_name` - (`string`, optional, defaults to `var.resource_group_name`) name of a Resource Group that + contains public IP that that will be associated with the interface. Used only when + `create_public_ip` is `false`. + - `public_ip_id` - (`string`, optional, defaults to `null`) ID of the public IP to associate with the + interface. Property is used when public IP is not created or sourced within this module. - `attach_to_lb_backend_pool` - (`bool`, optional, defaults to `false`) set to `true` if you would like to associate this interface with a Load Balancer backend pool. - `lb_backend_pool_id` - (`string`, optional, defaults to `null`) ID of an existing backend pool to associate the @@ -327,8 +331,13 @@ Example: { name = "fw-mgmt" subnet_id = azurerm_subnet.my_mgmt_subnet.id - public_ip_name = "fw-mgmt-pip" - create_public_ip = true + ip_configurations = { + primary-ip = { + name = "primary-ip" + primary = true + create_public_ip = true + public_ip_name = "fw-mgmt-pip" + } }, # public interface reusing an existing public IP resource { @@ -336,8 +345,35 @@ Example: subnet_id = azurerm_subnet.my_pub_subnet.id attach_to_lb_backend_pool = true lb_backend_pool_id = module.inbound_lb.backend_pool_id - create_public_ip = false - public_ip_name = "fw-public-pip" + ip_configurations = { + primary-ip = { + name = "primary-ip" + primary = true + create_public_ip = false + public_ip_name = "fw-public-pip" + } + }, + # interface with 2 IP addresses + { + name = "fw-two-ips" + subnet_id = azurerm_subnet.my_pub_subnet.id + attach_to_lb_backend_pool = true + lb_backend_pool_id = module.inbound_lb.backend_pool_id + ip_configurations = { + primary-ip = { + name = "primary-ip" + primary = true + create_public_ip = false + private_ip_address = "10.0.0.5" + public_ip_name = "fw-public-pip" + }, + secondary-ip = { + name = "secondary-ip" + primary = false + create_public_ip = false + private_ip_address = "10.0.0.6" + public_ip_name = "fw-public-pip" + } }, ] ``` @@ -347,18 +383,21 @@ Type: ```hcl list(object({ - name = string - subnet_id = string - ip_configuration_name = optional(string, "primary") - create_public_ip = optional(bool, false) - public_ip_name = optional(string) - public_ip_resource_group_name = optional(string) - public_ip_id = optional(string) - private_ip_address = optional(string) - lb_backend_pool_id = optional(string) - attach_to_lb_backend_pool = optional(bool, false) - appgw_backend_pool_id = optional(string) - attach_to_appgw_backend_pool = optional(bool, false) + name = string + subnet_id = string + ip_configurations = map(object({ + name = optional(string, "primary") + primary = optional(bool, true) + create_public_ip = optional(bool, false) + public_ip_name = optional(string) + public_ip_resource_group_name = optional(string) + public_ip_id = optional(string) + private_ip_address = optional(string) + })) + lb_backend_pool_id = optional(string) + attach_to_lb_backend_pool = optional(bool, false) + appgw_backend_pool_id = optional(string) + attach_to_appgw_backend_pool = optional(bool, false) })) ``` diff --git a/products/terraform/docs/swfw/azure/cloudngfw/modules/vmss.md b/products/terraform/docs/swfw/azure/cloudngfw/modules/vmss.md index 57ee7e8d3..5598afc27 100644 --- a/products/terraform/docs/swfw/azure/cloudngfw/modules/vmss.md +++ b/products/terraform/docs/swfw/azure/cloudngfw/modules/vmss.md @@ -54,6 +54,19 @@ probe would target the management interface which could lead to false-positives. probes, while the data plane remains unconfigured. An easy solution would to bo configure an interface swap, unfortunately this is not available in the Azure VM-Series image yet. +## Orchestration modes + +This module allows you to deploy a Virtual Machine Scale Set (VMSS) with a configurable orchestration mode. The choice of mode is +determined by the `orchestration_type` variable, which provides a single point of control over the VMSS deployment. + +Uniform Orchestration: if `orchestration_type` is set to `Uniform`, traditional uniform orchestration mode is used, where all +VMs are managed as a single entity. + +Flexible Orchestration: if `orchestration_type` is set to `Flexible`, new flexible orchestration mode is used. For this mode to +function correctly, the VMSS's identity type must be set to `UserAssigned` in order to manage individual VMs in the set. + +By default, the Uniform Orchestration is used by the module. + ## Custom Metrics and Autoscaling Firewalls can publish custom metrics (for example `panSessionUtilization`) to Azure Application Insights to improve the @@ -144,6 +157,8 @@ Name | Version | Source | Description - `linux_virtual_machine_scale_set` (managed) - `monitor_autoscale_setting` (managed) +- `orchestrated_virtual_machine_scale_set` (managed) +- `user_assigned_identity` (managed) - `public_ip_prefix` (data) ### Required Inputs @@ -292,22 +307,26 @@ Following configuration options are available: - `name` - (`string`, required) the interface name. - `subnet_id` - (`string`, required) ID of an existing subnet to create the interface in. -- `create_public_ip` - (`bool`, optional, defaults to `false`) if `true`, create a public IP for the interface. -- `pip_domain_name_label` - (`string`, optional, defaults to `null`) the Prefix which should be used for the Domain - Name Label for each Virtual Machine Instance. -- `pip_idle_timeout_in_minutes` - (`number`, optional, defaults to Azure default) the Idle Timeout in minutes for the Public - IP Address, possible values are in the range from 4 to 32. -- `pip_prefix_name` - (`string`, optional) the name of an existing Public IP Address Prefix from where Public IP - Addresses should be allocated. -- `pip_prefix_resource_group_name` - (`string`, optional, defaults to the VMSS's RG) name of a Resource Group hosting an - existing Public IP Prefix resource. -- `pip_prefix_id` - (`string`, optional) you can specify Public IP Prefix ID as an alternative to the - properties above (name and resource group), in case you want to avoid using a data source - block. +- `ip_configurations` - (`map`, required) A map that contains the IP configurations for the interface. + - `name` - (`string`, optional, defaults to `primary`) the name of the interface IP configuration. + - `primary` - (`bool`, optional, defaults to `true`) sets the current IP configuration as the primary + one. + - `create_public_ip` - (`bool`, optional, defaults to `false`) if `true`, create a public IP for the interface. + - `pip_domain_name_label` - (`string`, optional, defaults to `null`) the Prefix which should be used for the Domain + Name Label for each Virtual Machine Instance. + - `pip_idle_timeout_in_minutes` - (`number`, optional, defaults to Azure default) the Idle Timeout in minutes for the + Public IP Address, possible values are in the range from 4 to 32. + - `pip_prefix_name` - (`string`, optional) the name of an existing Public IP Address Prefix from where Public + IP Addresses should be allocated. + - `pip_prefix_resource_group_name` - (`string`, optional, defaults to the VMSS's RG) name of a Resource Group hosting an + existing Public IP Prefix resource. + - `pip_prefix_id` - (`string`, optional) you can specify Public IP Prefix ID as an alternative to the + properties above (name and resource group), in case you want to avoid using a data + source block. - `lb_backend_pool_ids` - (`list`, optional, defaults to `[]`) a list of identifiers of existing Load Balancer - backend pools to associate the interface with. + backend pools to associate the interface with. Only applied to primary IP configuration. - `appgw_backend_pool_ids` - (`list`, optional, defaults to `[]`) a list of identifier of Application Gateway's backend - pools to associate the interface with. + pools to associate the interface with. Only applied to primary IP configuration. Example: @@ -316,16 +335,33 @@ Example: { name = "management" subnet_id = azurerm_subnet.my_mgmt_subnet.id - create_pip = true + ip_configurations = { + primary-ip = { + name = "primary-ip" + primary = true + create_public_ip = true + } }, { name = "private" subnet_id = azurerm_subnet.my_priv_subnet.id + ip_configurations = { + primary-ip = { + name = "primary-ip" + primary = true + create_public_ip = false + } }, { name = "public" subnet_id = azurerm_subnet.my_pub_subnet.id lb_backend_pool_ids = [azurerm_lb_backend_address_pool.lb_backend.id] + ip_configurations = { + primary-ip = { + name = "primary-ip" + primary = true + create_public_ip = true + } } ] ``` @@ -335,16 +371,20 @@ Type: ```hcl list(object({ - name = string - subnet_id = string - create_public_ip = optional(bool, false) - pip_domain_name_label = optional(string) - pip_idle_timeout_in_minutes = optional(number) - pip_prefix_name = optional(string) - pip_prefix_resource_group_name = optional(string) - pip_prefix_id = optional(string) - lb_backend_pool_ids = optional(list(string), []) - appgw_backend_pool_ids = optional(list(string), []) + name = string + subnet_id = string + ip_configurations = map(object({ + name = optional(string, "primary") + primary = optional(bool, true) + create_public_ip = optional(bool, false) + pip_domain_name_label = optional(string) + pip_idle_timeout_in_minutes = optional(number) + pip_prefix_name = optional(string) + pip_prefix_resource_group_name = optional(string) + pip_prefix_id = optional(string) + })) + lb_backend_pool_ids = optional(list(string), []) + appgw_backend_pool_ids = optional(list(string), []) })) ``` @@ -372,14 +412,16 @@ Nevertheless they should be at least reviewed to meet deployment requirements. List of either required or important properties: -- `size` - (`string`, optional, defaults to `Standard_D3_v2`) Azure VM size (type). Consult the *VM-Series - Deployment Guide* as only few selected sizes are supported. The default one is a VM-300 equivalent. -- `zones` - (`list`, optional, defaults to `null`) a list of Availability Zones in which VMs from this Scale Set - will be created. -- `disk_type` - (`string`, optional, defaults to `StandardSSD_LRS`) type of Managed Disk which should be created, - possible values are `Standard_LRS`, `StandardSSD_LRS` or `Premium_LRS` (works only for selected `size` - values). -- `bootstrap_options` - (`string`, optional) bootstrap options to pass to VM-Series instance. +- `orchestration_type` - (`string`, optional, defaults to `Uniform`) this variable is used to select between the Uniform or + Flexible Scale Set orchestration modes. Possible values are `Uniform` and `Flexible`. +- `size` - (`string`, optional, defaults to `Standard_D3_v2`) Azure VM size (type). Consult the *VM-Series + Deployment Guide* as only few selected sizes are supported. The default one is a VM-300 equivalent. +- `zones` - (`list`, optional, defaults to `null`) a list of Availability Zones in which VMs from this Scale Set + will be created. Zone balance is available from at least 2 zones. +- `disk_type` - (`string`, optional, defaults to `StandardSSD_LRS`) type of Managed Disk which should be created, + possible values are `Standard_LRS`, `StandardSSD_LRS` or `Premium_LRS` (works only for selected `size` + values). +- `bootstrap_options` - (`string`, optional) bootstrap options to pass to VM-Series instance. Proper syntax is a string of semicolon separated properties, for example: @@ -399,9 +441,11 @@ List of other, optional properties: used to encrypt this VM's disk. - `encryption_at_host_enabled` - (`bool`, optional, defaults to Azure defaults) should all of disks be encrypted by enabling Encryption at Host. -- `overprovision` - (`bool`, optional, defaults to `true`) See the [provider documentation](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/linux_virtual_machine_scale_set). -- `platform_fault_domain_count` - (`number`, optional, defaults to Azure defaults) specifies the number of fault domains that - are used by this Virtual Machine Scale Set. +- `overprovision` - (`bool`, optional, defaults to `true`) controls whether Azure should over-provision Virtual + Machines in the Scale Set for improved deployment time and provisioning success rate. +- `platform_fault_domain_count` - (`number`, optional, defaults to `5`) specifies the number of fault domains that are used + by this Virtual Machine Scale Set. The Flexible orchestration mode requires this parameter + to be set. - `single_placement_group` - (`bool`, optional, defaults to Azure defaults) when `true` this Virtual Machine Scale Set will be limited to a Single Placement Group, which means the number of instances will be capped at 100 Virtual Machines. @@ -412,7 +456,8 @@ List of other, optional properties: files, when skipped a managed Storage Account will be used (preferred). - `identity_type` - (`string`, optional, defaults to `SystemAssigned`) type of Managed Service Identity that should be configured on this VM. Can be one of "SystemAssigned", "UserAssigned" or - "SystemAssigned, UserAssigned". + "SystemAssigned, UserAssigned". For the Flexible orchestration mode this parameter must be + configured to "UserAssigned". - `identity_ids` - (`list`, optional, defaults to `[]`) a list of User Assigned Managed Identity IDs to be assigned to this VM. Required only if `identity_type` is not "SystemAssigned". @@ -421,6 +466,7 @@ Type: ```hcl object({ + orchestration_type = optional(string, "Uniform") size = optional(string, "Standard_D3_v2") bootstrap_options = optional(string) zones = optional(list(string)) @@ -429,7 +475,7 @@ object({ allow_extension_operations = optional(bool, false) encryption_at_host_enabled = optional(bool) overprovision = optional(bool, true) - platform_fault_domain_count = optional(number) + platform_fault_domain_count = optional(number, 5) single_placement_group = optional(bool) capacity_reservation_group_id = optional(string) disk_encryption_set_id = optional(string) diff --git a/products/terraform/docs/swfw/azure/cloudngfw/modules/vwan.md b/products/terraform/docs/swfw/azure/cloudngfw/modules/vwan.md index f709b5e3c..3c9a36248 100644 --- a/products/terraform/docs/swfw/azure/cloudngfw/modules/vwan.md +++ b/products/terraform/docs/swfw/azure/cloudngfw/modules/vwan.md @@ -377,6 +377,8 @@ Each object represents one Connection and supports the following properties: - `name` - (`string`, required) the name of the Connection, must be unique within the Virtual Hub. - `connection_type` - (`string`, required) the type of Connection, use `Vnet` for Virtual Network connections. - `remote_virtual_network_id` - (`string`, optional) the resource ID of a remote Virtual Network. +- `internet_security_enabled` - (`bool`, optional) the parameter that enables internet-bound traffic from the connected VNet + to be routed through the Virtual Hub for inspection by a Network Virtual Appliance (NVA). - `hub_key` - (`string`, required) the key referencing the Virtual Hub. - `vpn_site_key` - (`string`, optional) the key referencing the VPN Site used in this Connection. - `vpn_link` - (`list`, optional, defaults to `[]`) list of VPN link configurations, each object supports the @@ -428,6 +430,7 @@ map(object({ connection_type = string hub_key = string remote_virtual_network_id = optional(string) + internet_security_enabled = optional(bool) vpn_site_key = optional(string) vpn_link = optional(list(object({ vpn_link_name = string diff --git a/products/terraform/docs/swfw/azure/cloudngfw/reference-architectures/cloudngfw_centralized_single_vwan.md b/products/terraform/docs/swfw/azure/cloudngfw/reference-architectures/cloudngfw_centralized_single_vwan.md index 8d445bb08..1d7e08c5e 100644 --- a/products/terraform/docs/swfw/azure/cloudngfw/reference-architectures/cloudngfw_centralized_single_vwan.md +++ b/products/terraform/docs/swfw/azure/cloudngfw/reference-architectures/cloudngfw_centralized_single_vwan.md @@ -816,14 +816,15 @@ map(object({ private_ip_address = optional(string) gwlb_key = optional(string) in_rules = optional(map(object({ - name = string - protocol = string - port = number - backend_port = optional(number) - health_probe_key = optional(string) - floating_ip = optional(bool) - session_persistence = optional(string) - nsg_priority = optional(number) + name = string + protocol = string + port = number + backend_port = optional(number) + health_probe_key = optional(string) + floating_ip = optional(bool) + session_persistence = optional(string) + nsg_priority = optional(number) + idle_timeout_in_minutes = optional(number) })), {}) out_rules = optional(map(object({ name = string diff --git a/products/terraform/docs/swfw/azure/cloudngfw/reference-architectures/cloudngfw_centralized_vnet.md b/products/terraform/docs/swfw/azure/cloudngfw/reference-architectures/cloudngfw_centralized_vnet.md index ee67bfe86..7d0ecadf3 100644 --- a/products/terraform/docs/swfw/azure/cloudngfw/reference-architectures/cloudngfw_centralized_vnet.md +++ b/products/terraform/docs/swfw/azure/cloudngfw/reference-architectures/cloudngfw_centralized_vnet.md @@ -816,14 +816,15 @@ map(object({ private_ip_address = optional(string) gwlb_key = optional(string) in_rules = optional(map(object({ - name = string - protocol = string - port = number - backend_port = optional(number) - health_probe_key = optional(string) - floating_ip = optional(bool) - session_persistence = optional(string) - nsg_priority = optional(number) + name = string + protocol = string + port = number + backend_port = optional(number) + health_probe_key = optional(string) + floating_ip = optional(bool) + session_persistence = optional(string) + nsg_priority = optional(number) + idle_timeout_in_minutes = optional(number) })), {}) out_rules = optional(map(object({ name = string diff --git a/products/terraform/docs/swfw/azure/cloudngfw/reference-architectures/cloudngfw_distributed.md b/products/terraform/docs/swfw/azure/cloudngfw/reference-architectures/cloudngfw_distributed.md index 0c0f04378..1f06881f3 100644 --- a/products/terraform/docs/swfw/azure/cloudngfw/reference-architectures/cloudngfw_distributed.md +++ b/products/terraform/docs/swfw/azure/cloudngfw/reference-architectures/cloudngfw_distributed.md @@ -807,14 +807,15 @@ map(object({ private_ip_address = optional(string) gwlb_key = optional(string) in_rules = optional(map(object({ - name = string - protocol = string - port = number - backend_port = optional(number) - health_probe_key = optional(string) - floating_ip = optional(bool) - session_persistence = optional(string) - nsg_priority = optional(number) + name = string + protocol = string + port = number + backend_port = optional(number) + health_probe_key = optional(string) + floating_ip = optional(bool) + session_persistence = optional(string) + nsg_priority = optional(number) + idle_timeout_in_minutes = optional(number) })), {}) out_rules = optional(map(object({ name = string diff --git a/products/terraform/docs/swfw/azure/vmseries/modules/loadbalancer.md b/products/terraform/docs/swfw/azure/vmseries/modules/loadbalancer.md index a7a723d0a..d09d47ef1 100644 --- a/products/terraform/docs/swfw/azure/vmseries/modules/loadbalancer.md +++ b/products/terraform/docs/swfw/azure/vmseries/modules/loadbalancer.md @@ -111,11 +111,11 @@ module "lbe" { ### Requirements - `terraform`, version: >= 1.5, < 2.0 -- `azurerm`, version: ~> 4.0 +- `azurerm`, version: ~> 4.42 ### Providers -- `azurerm`, version: ~> 4.0 +- `azurerm`, version: ~> 4.42 @@ -344,14 +344,15 @@ map(object({ private_ip_address = optional(string) gwlb_fip_id = optional(string) in_rules = optional(map(object({ - name = string - protocol = string - port = number - backend_port = optional(number) - health_probe_key = optional(string, "default") - floating_ip = optional(bool, true) - session_persistence = optional(string, "Default") - nsg_priority = optional(number) + name = string + protocol = string + port = number + backend_port = optional(number) + health_probe_key = optional(string, "default") + floating_ip = optional(bool, true) + session_persistence = optional(string, "Default") + nsg_priority = optional(number) + idle_timeout_in_minutes = optional(number) })), {}) out_rules = optional(map(object({ name = string diff --git a/products/terraform/docs/swfw/azure/vmseries/modules/vmseries.md b/products/terraform/docs/swfw/azure/vmseries/modules/vmseries.md index ddcd4ff32..dc9d3f1a1 100644 --- a/products/terraform/docs/swfw/azure/vmseries/modules/vmseries.md +++ b/products/terraform/docs/swfw/azure/vmseries/modules/vmseries.md @@ -212,7 +212,7 @@ Firewall parameters configuration. This map contains basic, as well as some optional Firewall parameters. Both types contain sane defaults. Nevertheless they should be at least reviewed to meet deployment requirements. -List of either required or important properties: +List of either required or important properties: - `size` - (`string`, optional, defaults to `Standard_D3_v2`) Azure VM size (type). Consult the *VM-Series Deployment Guide* as only a few selected sizes are supported. @@ -231,7 +231,7 @@ List of either required or important properties: For more details on bootstrapping [see documentation](https://docs.paloaltonetworks.com/vm-series/10-2/vm-series-deployment/bootstrap-the-vm-series-firewall/create-the-init-cfgtxt-file/init-cfgtxt-file-components). -List of other, optional properties: +List of other, optional properties: - `avset_id` - (`string`, optional, default to `null`) identifier of the Availability Set to use. - `capacity_reservation_group_id` - (`string`, optional, defaults to `null`) specifies the ID of the Capacity Reservation Group @@ -250,7 +250,7 @@ List of other, optional properties: - `identity_type` - (`string`, optional, defaults to `SystemAssigned`) type of Managed Service Identity that should be configured on this VM. Can be one of "SystemAssigned", "UserAssigned" or "SystemAssigned, UserAssigned". -- `identity_ids` - (`list`, optional, defaults to `[]`) a list of User Assigned Managed Identity IDs to be +- `identity_ids` - (`list`, optional, defaults to `[]`) a list of User Assigned Managed Identity IDs to be assigned to this VM. Required only if `identity_type` is not "SystemAssigned". @@ -290,26 +290,30 @@ Interfaces will be attached to VM in the order you define here, therefore: - The first should be the management interface, which does not participate in data filtering. - The remaining ones are the dataplane interfaces. - + Following configuration options are available: - `name` - (`string`, required) the interface name. - `subnet_id` - (`string`, required) ID of an existing subnet to create the interface in. -- `ip_configuration_name` - (`string`, optional, defaults to `primary`) the name of the interface IP configuration. -- `private_ip_address` - (`string`, optional, defaults to `null`) static private IP to assign to the interface. When - skipped Azure will assign one dynamically. Keep in mind that a dynamic IP is guarantied not - to change as long as the VM is running. Any stop/deallocate/restart operation might cause - the IP to change. -- `create_public_ip` - (`bool`, optional, defaults to `false`) if `true`, creates a public IP for the interface. -- `public_ip_name` - (`string`, optional, defaults to `null`) name of the public IP to associate with the - interface. When `create_public_ip` is set to `true` this will become a name of a newly - created Public IP interface. Otherwise this is a name of an existing interfaces that will - be sourced and attached to the interface. Not used when using `public_ip` module. -- `public_ip_resource_group_name` - (`string`, optional, defaults to `var.resource_group_name`) name of a Resource Group that - contains public IP that that will be associated with the interface. Used only when - `create_public_ip` is `false`. -- `public_ip_id` - (`string`, optional, defaults to `null`) ID of the public IP to associate with the - interface. Property is used when public IP is not created or sourced within this module. +- ip_configurations - (`map`, required) A map that contains the IP configurations for the interface. + - `name` - (`string`, optional, defaults to `primary`) the name of the interface IP configuration. + - `primary` - (`bool`, optional, defaults to `true`) sets the current IP configuration as the primary + one. + - `private_ip_address` - (`string`, optional, defaults to `null`) static private IP to assign to the interface. + When skipped Azure will assign one dynamically. Keep in mind that a dynamic IP is + guaranteed not to change as long as the VM is running. Any stop/deallocate/restart + operation might cause the IP to change. + - `create_public_ip` - (`bool`, optional, defaults to `false`) if `true`, creates a public IP for the interface. + **Note!** When you define multiple IP configurations, exactly one must be the primary. + - `public_ip_name` - (`string`, optional, defaults to `null`) name of the public IP to associate with the + interface. When `create_public_ip` is set to `true` this will become a name of a newly + created Public IP interface. Otherwise this is a name of an existing interfaces that will + be sourced and attached to the interface. Not used when using `public_ip` module. + - `public_ip_resource_group_name` - (`string`, optional, defaults to `var.resource_group_name`) name of a Resource Group that + contains public IP that that will be associated with the interface. Used only when + `create_public_ip` is `false`. + - `public_ip_id` - (`string`, optional, defaults to `null`) ID of the public IP to associate with the + interface. Property is used when public IP is not created or sourced within this module. - `attach_to_lb_backend_pool` - (`bool`, optional, defaults to `false`) set to `true` if you would like to associate this interface with a Load Balancer backend pool. - `lb_backend_pool_id` - (`string`, optional, defaults to `null`) ID of an existing backend pool to associate the @@ -327,8 +331,13 @@ Example: { name = "fw-mgmt" subnet_id = azurerm_subnet.my_mgmt_subnet.id - public_ip_name = "fw-mgmt-pip" - create_public_ip = true + ip_configurations = { + primary-ip = { + name = "primary-ip" + primary = true + create_public_ip = true + public_ip_name = "fw-mgmt-pip" + } }, # public interface reusing an existing public IP resource { @@ -336,8 +345,35 @@ Example: subnet_id = azurerm_subnet.my_pub_subnet.id attach_to_lb_backend_pool = true lb_backend_pool_id = module.inbound_lb.backend_pool_id - create_public_ip = false - public_ip_name = "fw-public-pip" + ip_configurations = { + primary-ip = { + name = "primary-ip" + primary = true + create_public_ip = false + public_ip_name = "fw-public-pip" + } + }, + # interface with 2 IP addresses + { + name = "fw-two-ips" + subnet_id = azurerm_subnet.my_pub_subnet.id + attach_to_lb_backend_pool = true + lb_backend_pool_id = module.inbound_lb.backend_pool_id + ip_configurations = { + primary-ip = { + name = "primary-ip" + primary = true + create_public_ip = false + private_ip_address = "10.0.0.5" + public_ip_name = "fw-public-pip" + }, + secondary-ip = { + name = "secondary-ip" + primary = false + create_public_ip = false + private_ip_address = "10.0.0.6" + public_ip_name = "fw-public-pip" + } }, ] ``` @@ -347,18 +383,21 @@ Type: ```hcl list(object({ - name = string - subnet_id = string - ip_configuration_name = optional(string, "primary") - create_public_ip = optional(bool, false) - public_ip_name = optional(string) - public_ip_resource_group_name = optional(string) - public_ip_id = optional(string) - private_ip_address = optional(string) - lb_backend_pool_id = optional(string) - attach_to_lb_backend_pool = optional(bool, false) - appgw_backend_pool_id = optional(string) - attach_to_appgw_backend_pool = optional(bool, false) + name = string + subnet_id = string + ip_configurations = map(object({ + name = optional(string, "primary") + primary = optional(bool, true) + create_public_ip = optional(bool, false) + public_ip_name = optional(string) + public_ip_resource_group_name = optional(string) + public_ip_id = optional(string) + private_ip_address = optional(string) + })) + lb_backend_pool_id = optional(string) + attach_to_lb_backend_pool = optional(bool, false) + appgw_backend_pool_id = optional(string) + attach_to_appgw_backend_pool = optional(bool, false) })) ``` diff --git a/products/terraform/docs/swfw/azure/vmseries/modules/vmss.md b/products/terraform/docs/swfw/azure/vmseries/modules/vmss.md index 57ee7e8d3..5598afc27 100644 --- a/products/terraform/docs/swfw/azure/vmseries/modules/vmss.md +++ b/products/terraform/docs/swfw/azure/vmseries/modules/vmss.md @@ -54,6 +54,19 @@ probe would target the management interface which could lead to false-positives. probes, while the data plane remains unconfigured. An easy solution would to bo configure an interface swap, unfortunately this is not available in the Azure VM-Series image yet. +## Orchestration modes + +This module allows you to deploy a Virtual Machine Scale Set (VMSS) with a configurable orchestration mode. The choice of mode is +determined by the `orchestration_type` variable, which provides a single point of control over the VMSS deployment. + +Uniform Orchestration: if `orchestration_type` is set to `Uniform`, traditional uniform orchestration mode is used, where all +VMs are managed as a single entity. + +Flexible Orchestration: if `orchestration_type` is set to `Flexible`, new flexible orchestration mode is used. For this mode to +function correctly, the VMSS's identity type must be set to `UserAssigned` in order to manage individual VMs in the set. + +By default, the Uniform Orchestration is used by the module. + ## Custom Metrics and Autoscaling Firewalls can publish custom metrics (for example `panSessionUtilization`) to Azure Application Insights to improve the @@ -144,6 +157,8 @@ Name | Version | Source | Description - `linux_virtual_machine_scale_set` (managed) - `monitor_autoscale_setting` (managed) +- `orchestrated_virtual_machine_scale_set` (managed) +- `user_assigned_identity` (managed) - `public_ip_prefix` (data) ### Required Inputs @@ -292,22 +307,26 @@ Following configuration options are available: - `name` - (`string`, required) the interface name. - `subnet_id` - (`string`, required) ID of an existing subnet to create the interface in. -- `create_public_ip` - (`bool`, optional, defaults to `false`) if `true`, create a public IP for the interface. -- `pip_domain_name_label` - (`string`, optional, defaults to `null`) the Prefix which should be used for the Domain - Name Label for each Virtual Machine Instance. -- `pip_idle_timeout_in_minutes` - (`number`, optional, defaults to Azure default) the Idle Timeout in minutes for the Public - IP Address, possible values are in the range from 4 to 32. -- `pip_prefix_name` - (`string`, optional) the name of an existing Public IP Address Prefix from where Public IP - Addresses should be allocated. -- `pip_prefix_resource_group_name` - (`string`, optional, defaults to the VMSS's RG) name of a Resource Group hosting an - existing Public IP Prefix resource. -- `pip_prefix_id` - (`string`, optional) you can specify Public IP Prefix ID as an alternative to the - properties above (name and resource group), in case you want to avoid using a data source - block. +- `ip_configurations` - (`map`, required) A map that contains the IP configurations for the interface. + - `name` - (`string`, optional, defaults to `primary`) the name of the interface IP configuration. + - `primary` - (`bool`, optional, defaults to `true`) sets the current IP configuration as the primary + one. + - `create_public_ip` - (`bool`, optional, defaults to `false`) if `true`, create a public IP for the interface. + - `pip_domain_name_label` - (`string`, optional, defaults to `null`) the Prefix which should be used for the Domain + Name Label for each Virtual Machine Instance. + - `pip_idle_timeout_in_minutes` - (`number`, optional, defaults to Azure default) the Idle Timeout in minutes for the + Public IP Address, possible values are in the range from 4 to 32. + - `pip_prefix_name` - (`string`, optional) the name of an existing Public IP Address Prefix from where Public + IP Addresses should be allocated. + - `pip_prefix_resource_group_name` - (`string`, optional, defaults to the VMSS's RG) name of a Resource Group hosting an + existing Public IP Prefix resource. + - `pip_prefix_id` - (`string`, optional) you can specify Public IP Prefix ID as an alternative to the + properties above (name and resource group), in case you want to avoid using a data + source block. - `lb_backend_pool_ids` - (`list`, optional, defaults to `[]`) a list of identifiers of existing Load Balancer - backend pools to associate the interface with. + backend pools to associate the interface with. Only applied to primary IP configuration. - `appgw_backend_pool_ids` - (`list`, optional, defaults to `[]`) a list of identifier of Application Gateway's backend - pools to associate the interface with. + pools to associate the interface with. Only applied to primary IP configuration. Example: @@ -316,16 +335,33 @@ Example: { name = "management" subnet_id = azurerm_subnet.my_mgmt_subnet.id - create_pip = true + ip_configurations = { + primary-ip = { + name = "primary-ip" + primary = true + create_public_ip = true + } }, { name = "private" subnet_id = azurerm_subnet.my_priv_subnet.id + ip_configurations = { + primary-ip = { + name = "primary-ip" + primary = true + create_public_ip = false + } }, { name = "public" subnet_id = azurerm_subnet.my_pub_subnet.id lb_backend_pool_ids = [azurerm_lb_backend_address_pool.lb_backend.id] + ip_configurations = { + primary-ip = { + name = "primary-ip" + primary = true + create_public_ip = true + } } ] ``` @@ -335,16 +371,20 @@ Type: ```hcl list(object({ - name = string - subnet_id = string - create_public_ip = optional(bool, false) - pip_domain_name_label = optional(string) - pip_idle_timeout_in_minutes = optional(number) - pip_prefix_name = optional(string) - pip_prefix_resource_group_name = optional(string) - pip_prefix_id = optional(string) - lb_backend_pool_ids = optional(list(string), []) - appgw_backend_pool_ids = optional(list(string), []) + name = string + subnet_id = string + ip_configurations = map(object({ + name = optional(string, "primary") + primary = optional(bool, true) + create_public_ip = optional(bool, false) + pip_domain_name_label = optional(string) + pip_idle_timeout_in_minutes = optional(number) + pip_prefix_name = optional(string) + pip_prefix_resource_group_name = optional(string) + pip_prefix_id = optional(string) + })) + lb_backend_pool_ids = optional(list(string), []) + appgw_backend_pool_ids = optional(list(string), []) })) ``` @@ -372,14 +412,16 @@ Nevertheless they should be at least reviewed to meet deployment requirements. List of either required or important properties: -- `size` - (`string`, optional, defaults to `Standard_D3_v2`) Azure VM size (type). Consult the *VM-Series - Deployment Guide* as only few selected sizes are supported. The default one is a VM-300 equivalent. -- `zones` - (`list`, optional, defaults to `null`) a list of Availability Zones in which VMs from this Scale Set - will be created. -- `disk_type` - (`string`, optional, defaults to `StandardSSD_LRS`) type of Managed Disk which should be created, - possible values are `Standard_LRS`, `StandardSSD_LRS` or `Premium_LRS` (works only for selected `size` - values). -- `bootstrap_options` - (`string`, optional) bootstrap options to pass to VM-Series instance. +- `orchestration_type` - (`string`, optional, defaults to `Uniform`) this variable is used to select between the Uniform or + Flexible Scale Set orchestration modes. Possible values are `Uniform` and `Flexible`. +- `size` - (`string`, optional, defaults to `Standard_D3_v2`) Azure VM size (type). Consult the *VM-Series + Deployment Guide* as only few selected sizes are supported. The default one is a VM-300 equivalent. +- `zones` - (`list`, optional, defaults to `null`) a list of Availability Zones in which VMs from this Scale Set + will be created. Zone balance is available from at least 2 zones. +- `disk_type` - (`string`, optional, defaults to `StandardSSD_LRS`) type of Managed Disk which should be created, + possible values are `Standard_LRS`, `StandardSSD_LRS` or `Premium_LRS` (works only for selected `size` + values). +- `bootstrap_options` - (`string`, optional) bootstrap options to pass to VM-Series instance. Proper syntax is a string of semicolon separated properties, for example: @@ -399,9 +441,11 @@ List of other, optional properties: used to encrypt this VM's disk. - `encryption_at_host_enabled` - (`bool`, optional, defaults to Azure defaults) should all of disks be encrypted by enabling Encryption at Host. -- `overprovision` - (`bool`, optional, defaults to `true`) See the [provider documentation](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/linux_virtual_machine_scale_set). -- `platform_fault_domain_count` - (`number`, optional, defaults to Azure defaults) specifies the number of fault domains that - are used by this Virtual Machine Scale Set. +- `overprovision` - (`bool`, optional, defaults to `true`) controls whether Azure should over-provision Virtual + Machines in the Scale Set for improved deployment time and provisioning success rate. +- `platform_fault_domain_count` - (`number`, optional, defaults to `5`) specifies the number of fault domains that are used + by this Virtual Machine Scale Set. The Flexible orchestration mode requires this parameter + to be set. - `single_placement_group` - (`bool`, optional, defaults to Azure defaults) when `true` this Virtual Machine Scale Set will be limited to a Single Placement Group, which means the number of instances will be capped at 100 Virtual Machines. @@ -412,7 +456,8 @@ List of other, optional properties: files, when skipped a managed Storage Account will be used (preferred). - `identity_type` - (`string`, optional, defaults to `SystemAssigned`) type of Managed Service Identity that should be configured on this VM. Can be one of "SystemAssigned", "UserAssigned" or - "SystemAssigned, UserAssigned". + "SystemAssigned, UserAssigned". For the Flexible orchestration mode this parameter must be + configured to "UserAssigned". - `identity_ids` - (`list`, optional, defaults to `[]`) a list of User Assigned Managed Identity IDs to be assigned to this VM. Required only if `identity_type` is not "SystemAssigned". @@ -421,6 +466,7 @@ Type: ```hcl object({ + orchestration_type = optional(string, "Uniform") size = optional(string, "Standard_D3_v2") bootstrap_options = optional(string) zones = optional(list(string)) @@ -429,7 +475,7 @@ object({ allow_extension_operations = optional(bool, false) encryption_at_host_enabled = optional(bool) overprovision = optional(bool, true) - platform_fault_domain_count = optional(number) + platform_fault_domain_count = optional(number, 5) single_placement_group = optional(bool) capacity_reservation_group_id = optional(string) disk_encryption_set_id = optional(string) diff --git a/products/terraform/docs/swfw/azure/vmseries/modules/vwan.md b/products/terraform/docs/swfw/azure/vmseries/modules/vwan.md index f709b5e3c..3c9a36248 100644 --- a/products/terraform/docs/swfw/azure/vmseries/modules/vwan.md +++ b/products/terraform/docs/swfw/azure/vmseries/modules/vwan.md @@ -377,6 +377,8 @@ Each object represents one Connection and supports the following properties: - `name` - (`string`, required) the name of the Connection, must be unique within the Virtual Hub. - `connection_type` - (`string`, required) the type of Connection, use `Vnet` for Virtual Network connections. - `remote_virtual_network_id` - (`string`, optional) the resource ID of a remote Virtual Network. +- `internet_security_enabled` - (`bool`, optional) the parameter that enables internet-bound traffic from the connected VNet + to be routed through the Virtual Hub for inspection by a Network Virtual Appliance (NVA). - `hub_key` - (`string`, required) the key referencing the Virtual Hub. - `vpn_site_key` - (`string`, optional) the key referencing the VPN Site used in this Connection. - `vpn_link` - (`list`, optional, defaults to `[]`) list of VPN link configurations, each object supports the @@ -428,6 +430,7 @@ map(object({ connection_type = string hub_key = string remote_virtual_network_id = optional(string) + internet_security_enabled = optional(bool) vpn_site_key = optional(string) vpn_link = optional(list(object({ vpn_link_name = string diff --git a/products/terraform/docs/swfw/azure/vmseries/reference-architectures/2ef99143-3dcf-4113-814d-aedca8fcccf3.png b/products/terraform/docs/swfw/azure/vmseries/reference-architectures/2ef99143-3dcf-4113-814d-aedca8fcccf3.png new file mode 100644 index 0000000000000000000000000000000000000000..2edea159e50bc960bc7894d2152947d335159b39 GIT binary patch literal 77848 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 zπ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%0D@^Tn=gD>SYPB6X`59*W-4Yo@L0-pO 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 LWvNU_!x#S#eWjrR literal 0 HcmV?d00001 diff --git a/products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_common.md b/products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_common.md index 6f0c7a98d..61d0e9d20 100644 --- a/products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_common.md +++ b/products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_common.md @@ -657,14 +657,15 @@ map(object({ private_ip_address = optional(string) gwlb_key = optional(string) in_rules = optional(map(object({ - name = string - protocol = string - port = number - backend_port = optional(number) - health_probe_key = optional(string) - floating_ip = optional(bool) - session_persistence = optional(string) - nsg_priority = optional(number) + name = string + protocol = string + port = number + backend_port = optional(number) + health_probe_key = optional(string) + floating_ip = optional(bool) + session_persistence = optional(string) + nsg_priority = optional(number) + idle_timeout_in_minutes = optional(number) })), {}) out_rules = optional(map(object({ name = string @@ -1026,16 +1027,16 @@ Default value: `map[]` #### vmseries_universal -A map defining common settings for all created VM-Series instances. - +A map defining common settings for all created VM-Series instances. + It duplicates popular properties from `vmseries` variable, specifically `vmseries.image` and `vmseries.virtual_machine` maps. However, if values are set in those maps, they still take precedence over the ones set within this variable. As a result, all universal properties can be overriden on a per-VM basis. Following properties are supported: -- `use_airs` - (`bool`, optional, defaults to `false`) when set to `true`, the AI Runtime Security VM image is used - instead of the one passed to the module and version for `airs-flex` offer must be provided. +- `use_airs` - (`bool`, optional, defaults to `false`) when set to `true`, the AI Runtime Security VM image is used + instead of the one passed to the module and version for `airs-flex` offer must be provided. - `version` - (`string`, optional) describes the PAN-OS image version from Azure Marketplace. - `size` - (`string`, optional, defaults to module default) Azure VM size (type). Consult the *VM-Series Deployment Guide* as only a few selected sizes are supported. @@ -1122,7 +1123,7 @@ The most basic properties are as follows: For all properties and their default values see [module's documentation](../../modules/vmseries#authentication). - `image` - (`map`, optional) properties defining a base image used by the deployed VM. The `image` property is - required (if no common properties were set within `vmseries_universal` variable) but there are only 2 + required (if no common properties were set within `vmseries_universal` variable) but there are only 2 properties (mutually exclusive) that have to be set, either: - `version` - (`string`, optional) describes the PAN-OS image version from Azure Marketplace. @@ -1199,7 +1200,15 @@ The most basic properties are as follows: - `name` - (`string`, required) name of the network interface (will be prefixed with `var.name_prefix`). - `subnet_key` - (`string`, required) a key of a subnet to which the interface will be assigned as defined in `var.vnets`. Key identifying the VNET is defined in `virtual_machine.vnet_key` property. - - `create_public_ip` - (`bool`, optional, defaults to `false`) create a Public IP for an interface. + - `ip_configurations` - (`map`, required) A map that contains the IP configurations for the interface. + - `name` - (`string`, optional, defaults to `primary`) the name of the interface IP configuration. + - `primary` - (`bool`, optional, defaults to `true`) sets the current IP configuration as the primary one. + **Note!** When you define multiple IP configurations, exactly one must be the primary. + - `private_ip_address` - (`string`, optional, defaults to `null`) static private IP to assign to the interface. When + skipped Azure will assign one dynamically. Keep in mind that a dynamic IP is guarantied not + to change as long as the VM is running. Any stop/deallocate/restart operation might cause + the IP to change. + - `create_public_ip` - (`bool`, optional, defaults to `false`) if `true`, creates a public IP for the interface. - `load_balancer_key` - (`string`, optional, defaults to `null`) key of a Load Balancer defined in `var.loadbalancers` variable, network interface that has this property defined will be added to the Load Balancer's backend pool. @@ -1288,17 +1297,20 @@ map(object({ identity_ids = optional(list(string)) }) interfaces = list(object({ - name = string - subnet_key = string - ip_configuration_name = optional(string) - create_public_ip = optional(bool, false) - public_ip_name = optional(string) - public_ip_resource_group_name = optional(string) - public_ip_key = optional(string) - private_ip_address = optional(string) - load_balancer_key = optional(string) - application_gateway_key = optional(string) - appgw_backend_pool_id = optional(string) + name = string + subnet_key = string + ip_configurations = map(object({ + name = optional(string) + primary = optional(bool, true) + create_public_ip = optional(bool, false) + public_ip_name = optional(string) + public_ip_resource_group_name = optional(string) + public_ip_key = optional(string) + private_ip_address = optional(string) + })) + load_balancer_key = optional(string) + application_gateway_key = optional(string) + appgw_backend_pool_id = optional(string) })) })) ``` @@ -1335,12 +1347,12 @@ Following properties are supported: [VNET module documentation](../../modules/vnet#route_tables). - `subnets` - (`map`, optional) map of Subnets to create or source, for details see [VNET module documentation](../../modules/vnet#subnets). - - `local_peer_config` - (`map`, optional) a map that contains local peer configuration parameters. This value allows to - set `allow_virtual_network_access`, `allow_forwarded_traffic`, `allow_gateway_transit` and - `use_remote_gateways` parameters on the local VNet peering. + - `local_peer_config` - (`map`, optional) a map that contains local peer configuration parameters. This value allows to + set `allow_virtual_network_access`, `allow_forwarded_traffic`, `allow_gateway_transit` and + `use_remote_gateways` parameters on the local VNet peering. - `remote_peer_config` - (`map`, optional) a map that contains remote peer configuration parameters. This value allows to - set `allow_virtual_network_access`, `allow_forwarded_traffic`, `allow_gateway_transit` and - `use_remote_gateways` parameters on the remote VNet peering. + set `allow_virtual_network_access`, `allow_forwarded_traffic`, `allow_gateway_transit` and + `use_remote_gateways` parameters on the remote VNet peering. For all properties and their default values see [module's documentation](../../modules/test_infrastructure#vnets). @@ -1504,14 +1516,15 @@ map(object({ private_ip_address = optional(string) gwlb_key = optional(string) in_rules = optional(map(object({ - name = string - protocol = string - port = number - backend_port = optional(number) - health_probe_key = optional(string) - floating_ip = optional(bool) - session_persistence = optional(string) - nsg_priority = optional(number) + name = string + protocol = string + port = number + backend_port = optional(number) + health_probe_key = optional(string) + floating_ip = optional(bool) + session_persistence = optional(string) + nsg_priority = optional(number) + idle_timeout_in_minutes = optional(number) })), {}) out_rules = optional(map(object({ name = string diff --git a/products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_common_autoscale.md b/products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_common_autoscale.md index ce164a1b4..bc7156708 100644 --- a/products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_common_autoscale.md +++ b/products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_common_autoscale.md @@ -684,14 +684,15 @@ map(object({ private_ip_address = optional(string) gwlb_key = optional(string) in_rules = optional(map(object({ - name = string - protocol = string - port = number - backend_port = optional(number) - health_probe_key = optional(string) - floating_ip = optional(bool) - session_persistence = optional(string) - nsg_priority = optional(number) + name = string + protocol = string + port = number + backend_port = optional(number) + health_probe_key = optional(string) + floating_ip = optional(bool) + session_persistence = optional(string) + nsg_priority = optional(number) + idle_timeout_in_minutes = optional(number) })), {}) out_rules = optional(map(object({ name = string @@ -1209,7 +1210,10 @@ The basic Scale Set configuration properties are as follows: - `name` - (`string`, required) name of the network interface (will be prefixed with `var.name_prefix`). - `subnet_key` - (`string`, required) a key of a subnet to which the interface will be assigned as defined in `var.vnets`. - - `create_public_ip` - (`bool`, optional, defaults to module default) create Public IP for an interface. + - `ip_configurations` - (`map`, required) A map that contains the IP configurations for the interface. + - `name` - (`string`, optional, defaults to `primary`) the name of the interface IP configuration. + - `primary` - (`bool`, optional, defaults to `true`) sets the current IP configuration as the primary one. + - `create_public_ip` - (`bool`, optional, defaults to `false`) if `true`, create a public IP for the interface. - `load_balancer_key` - (`string`, optional, defaults to `null`) key of a Load Balancer defined in the `var.loadbalancers` variable, network interface that has this property defined will be added to the Load Balancer's backend pool. @@ -1246,9 +1250,10 @@ map(object({ custom_id = optional(string) })) virtual_machine_scale_set = optional(object({ - size = optional(string) - zones = optional(list(string)) - disk_type = optional(string) + orchestration_type = optional(string) + size = optional(string) + zones = optional(list(string)) + disk_type = optional(string) bootstrap_options = optional(object({ type = optional(string) ip-address = optional(string) @@ -1308,16 +1313,20 @@ map(object({ webhooks_uris = optional(map(string), {}) }), {}) interfaces = list(object({ - name = string - subnet_key = string - create_public_ip = optional(bool) - pip_domain_name_label = optional(string) - pip_idle_timeout_in_minutes = optional(number) - pip_prefix_name = optional(string) - pip_prefix_resource_group_name = optional(string) - pip_prefix_id = optional(string) - load_balancer_key = optional(string) - application_gateway_key = optional(string) + name = string + subnet_key = string + ip_configurations = optional(map(object({ + name = optional(string) + primary = optional(bool, true) + create_public_ip = optional(bool, false) + pip_domain_name_label = optional(string) + pip_idle_timeout_in_minutes = optional(number) + pip_prefix_name = optional(string) + pip_prefix_resource_group_name = optional(string) + pip_prefix_id = optional(string) + }))) + load_balancer_key = optional(string) + application_gateway_key = optional(string) })) autoscaling_profiles = optional(list(object({ name = string @@ -1558,14 +1567,15 @@ map(object({ private_ip_address = optional(string) gwlb_key = optional(string) in_rules = optional(map(object({ - name = string - protocol = string - port = number - backend_port = optional(number) - health_probe_key = optional(string) - floating_ip = optional(bool) - session_persistence = optional(string) - nsg_priority = optional(number) + name = string + protocol = string + port = number + backend_port = optional(number) + health_probe_key = optional(string) + floating_ip = optional(bool) + session_persistence = optional(string) + nsg_priority = optional(number) + idle_timeout_in_minutes = optional(number) })), {}) out_rules = optional(map(object({ name = string diff --git a/products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_dedicated.md b/products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_dedicated.md index fb7bdafde..560204d31 100644 --- a/products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_dedicated.md +++ b/products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_dedicated.md @@ -661,14 +661,15 @@ map(object({ private_ip_address = optional(string) gwlb_key = optional(string) in_rules = optional(map(object({ - name = string - protocol = string - port = number - backend_port = optional(number) - health_probe_key = optional(string) - floating_ip = optional(bool) - session_persistence = optional(string) - nsg_priority = optional(number) + name = string + protocol = string + port = number + backend_port = optional(number) + health_probe_key = optional(string) + floating_ip = optional(bool) + session_persistence = optional(string) + nsg_priority = optional(number) + idle_timeout_in_minutes = optional(number) })), {}) out_rules = optional(map(object({ name = string @@ -1030,16 +1031,16 @@ Default value: `map[]` #### vmseries_universal -A map defining common settings for all created VM-Series instances. - +A map defining common settings for all created VM-Series instances. + It duplicates popular properties from `vmseries` variable, specifically `vmseries.image` and `vmseries.virtual_machine` maps. However, if values are set in those maps, they still take precedence over the ones set within this variable. As a result, all universal properties can be overriden on a per-VM basis. Following properties are supported: -- `use_airs` - (`bool`, optional, defaults to `false`) when set to `true`, the AI Runtime Security VM image is used - instead of the one passed to the module and version for `airs-flex` offer must be provided. +- `use_airs` - (`bool`, optional, defaults to `false`) when set to `true`, the AI Runtime Security VM image is used + instead of the one passed to the module and version for `airs-flex` offer must be provided. - `version` - (`string`, optional) describes the PAN-OS image version from Azure Marketplace. - `size` - (`string`, optional, defaults to module default) Azure VM size (type). Consult the *VM-Series Deployment Guide* as only a few selected sizes are supported. @@ -1126,7 +1127,7 @@ The most basic properties are as follows: For all properties and their default values see [module's documentation](../../modules/vmseries#authentication). - `image` - (`map`, optional) properties defining a base image used by the deployed VM. The `image` property is - required (if no common properties were set within `vmseries_universal` variable) but there are only 2 + required (if no common properties were set within `vmseries_universal` variable) but there are only 2 properties (mutually exclusive) that have to be set, either: - `version` - (`string`, optional) describes the PAN-OS image version from Azure Marketplace. @@ -1203,7 +1204,15 @@ The most basic properties are as follows: - `name` - (`string`, required) name of the network interface (will be prefixed with `var.name_prefix`). - `subnet_key` - (`string`, required) a key of a subnet to which the interface will be assigned as defined in `var.vnets`. Key identifying the VNET is defined in `virtual_machine.vnet_key` property. - - `create_public_ip` - (`bool`, optional, defaults to `false`) create a Public IP for an interface. + - `ip_configurations` - (`map`, required) A map that contains the IP configurations for the interface. + - `name` - (`string`, optional, defaults to `primary`) the name of the interface IP configuration. + - `primary` - (`bool`, optional, defaults to `true`) sets the current IP configuration as the primary one. + **Note!** When you define multiple IP configurations, exactly one must be the primary. + - `private_ip_address` - (`string`, optional, defaults to `null`) static private IP to assign to the interface. When + skipped Azure will assign one dynamically. Keep in mind that a dynamic IP is guarantied not + to change as long as the VM is running. Any stop/deallocate/restart operation might cause + the IP to change. + - `create_public_ip` - (`bool`, optional, defaults to `false`) if `true`, creates a public IP for the interface. - `load_balancer_key` - (`string`, optional, defaults to `null`) key of a Load Balancer defined in `var.loadbalancers` variable, network interface that has this property defined will be added to the Load Balancer's backend pool. @@ -1292,17 +1301,20 @@ map(object({ identity_ids = optional(list(string)) }) interfaces = list(object({ - name = string - subnet_key = string - ip_configuration_name = optional(string) - create_public_ip = optional(bool, false) - public_ip_name = optional(string) - public_ip_resource_group_name = optional(string) - public_ip_key = optional(string) - private_ip_address = optional(string) - load_balancer_key = optional(string) - application_gateway_key = optional(string) - appgw_backend_pool_id = optional(string) + name = string + subnet_key = string + ip_configurations = map(object({ + name = optional(string) + primary = optional(bool, true) + create_public_ip = optional(bool, false) + public_ip_name = optional(string) + public_ip_resource_group_name = optional(string) + public_ip_key = optional(string) + private_ip_address = optional(string) + })) + load_balancer_key = optional(string) + application_gateway_key = optional(string) + appgw_backend_pool_id = optional(string) })) })) ``` @@ -1339,12 +1351,12 @@ Following properties are supported: [VNET module documentation](../../modules/vnet#route_tables). - `subnets` - (`map`, optional) map of Subnets to create or source, for details see [VNET module documentation](../../modules/vnet#subnets). - - `local_peer_config` - (`map`, optional) a map that contains local peer configuration parameters. This value allows to - set `allow_virtual_network_access`, `allow_forwarded_traffic`, `allow_gateway_transit` and - `use_remote_gateways` parameters on the local VNet peering. + - `local_peer_config` - (`map`, optional) a map that contains local peer configuration parameters. This value allows to + set `allow_virtual_network_access`, `allow_forwarded_traffic`, `allow_gateway_transit` and + `use_remote_gateways` parameters on the local VNet peering. - `remote_peer_config` - (`map`, optional) a map that contains remote peer configuration parameters. This value allows to - set `allow_virtual_network_access`, `allow_forwarded_traffic`, `allow_gateway_transit` and - `use_remote_gateways` parameters on the remote VNet peering. + set `allow_virtual_network_access`, `allow_forwarded_traffic`, `allow_gateway_transit` and + `use_remote_gateways` parameters on the remote VNet peering. For all properties and their default values see [module's documentation](../../modules/test_infrastructure#vnets). @@ -1508,14 +1520,15 @@ map(object({ private_ip_address = optional(string) gwlb_key = optional(string) in_rules = optional(map(object({ - name = string - protocol = string - port = number - backend_port = optional(number) - health_probe_key = optional(string) - floating_ip = optional(bool) - session_persistence = optional(string) - nsg_priority = optional(number) + name = string + protocol = string + port = number + backend_port = optional(number) + health_probe_key = optional(string) + floating_ip = optional(bool) + session_persistence = optional(string) + nsg_priority = optional(number) + idle_timeout_in_minutes = optional(number) })), {}) out_rules = optional(map(object({ name = string diff --git a/products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_dedicated_autoscale.md b/products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_dedicated_autoscale.md index 4f4df4031..b3df58307 100644 --- a/products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_dedicated_autoscale.md +++ b/products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_dedicated_autoscale.md @@ -678,14 +678,15 @@ map(object({ private_ip_address = optional(string) gwlb_key = optional(string) in_rules = optional(map(object({ - name = string - protocol = string - port = number - backend_port = optional(number) - health_probe_key = optional(string) - floating_ip = optional(bool) - session_persistence = optional(string) - nsg_priority = optional(number) + name = string + protocol = string + port = number + backend_port = optional(number) + health_probe_key = optional(string) + floating_ip = optional(bool) + session_persistence = optional(string) + nsg_priority = optional(number) + idle_timeout_in_minutes = optional(number) })), {}) out_rules = optional(map(object({ name = string @@ -1026,7 +1027,7 @@ set within this variable. As a result, all universal properties can be overriden Following properties are supported: - `use_airs` - (`bool`, optional, defaults to `false`) when set to `true`, the AI Runtime Security VM image is used - instead of the one passed to the module and version for `airs-flex` offer must be provided. + instead of the one passed to the module and version for `airs-flex` offer must be provided. - `version` - (`string`, optional) describes the PAN-OS image version from Azure Marketplace. - `size` - (`string`, optional, defaults to module default) Azure VM size (type). Consult the *VM-Series Deployment Guide* as only a few selected sizes are supported. @@ -1203,7 +1204,10 @@ The basic Scale Set configuration properties are as follows: - `name` - (`string`, required) name of the network interface (will be prefixed with `var.name_prefix`). - `subnet_key` - (`string`, required) a key of a subnet to which the interface will be assigned as defined in `var.vnets`. - - `create_public_ip` - (`bool`, optional, defaults to module default) create Public IP for an interface. + - `ip_configurations` - (`map`, required) A map that contains the IP configurations for the interface. + - `name` - (`string`, optional, defaults to `primary`) the name of the interface IP configuration. + - `primary` - (`bool`, optional, defaults to `true`) sets the current IP configuration as the primary one. + - `create_public_ip` - (`bool`, optional, defaults to `false`) if `true`, create a public IP for the interface. - `load_balancer_key` - (`string`, optional, defaults to `null`) key of a Load Balancer defined in the `var.loadbalancers` variable, network interface that has this property defined will be added to the Load Balancer's backend pool. @@ -1240,9 +1244,10 @@ map(object({ custom_id = optional(string) })) virtual_machine_scale_set = optional(object({ - size = optional(string) - zones = optional(list(string)) - disk_type = optional(string) + orchestration_type = optional(string) + size = optional(string) + zones = optional(list(string)) + disk_type = optional(string) bootstrap_options = optional(object({ type = optional(string) ip-address = optional(string) @@ -1302,16 +1307,20 @@ map(object({ webhooks_uris = optional(map(string), {}) }), {}) interfaces = list(object({ - name = string - subnet_key = string - create_public_ip = optional(bool) - pip_domain_name_label = optional(string) - pip_idle_timeout_in_minutes = optional(number) - pip_prefix_name = optional(string) - pip_prefix_resource_group_name = optional(string) - pip_prefix_id = optional(string) - load_balancer_key = optional(string) - application_gateway_key = optional(string) + name = string + subnet_key = string + ip_configurations = optional(map(object({ + name = optional(string) + primary = optional(bool, true) + create_public_ip = optional(bool, false) + pip_domain_name_label = optional(string) + pip_idle_timeout_in_minutes = optional(number) + pip_prefix_name = optional(string) + pip_prefix_resource_group_name = optional(string) + pip_prefix_id = optional(string) + }))) + load_balancer_key = optional(string) + application_gateway_key = optional(string) })) autoscaling_profiles = optional(list(object({ name = string @@ -1552,14 +1561,15 @@ map(object({ private_ip_address = optional(string) gwlb_key = optional(string) in_rules = optional(map(object({ - name = string - protocol = string - port = number - backend_port = optional(number) - health_probe_key = optional(string) - floating_ip = optional(bool) - session_persistence = optional(string) - nsg_priority = optional(number) + name = string + protocol = string + port = number + backend_port = optional(number) + health_probe_key = optional(string) + floating_ip = optional(bool) + session_persistence = optional(string) + nsg_priority = optional(number) + idle_timeout_in_minutes = optional(number) })), {}) out_rules = optional(map(object({ name = string diff --git a/products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_dedicated_vwan.md b/products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_dedicated_vwan.md new file mode 100644 index 000000000..d80726bec --- /dev/null +++ b/products/terraform/docs/swfw/azure/vmseries/reference-architectures/vmseries_transit_vnet_dedicated_vwan.md @@ -0,0 +1,1703 @@ +--- +hide_title: true +id: vmseries_transit_vnet_dedicated_vwan +keywords: +- pan-os +- panos +- firewall +- configuration +- terraform +- vmseries +- vm-series +- swfw +- software-firewalls +- azure +pagination_next: null +pagination_prev: null +sidebar_label: VM-Series Transit VNet Dedicated Vwan +title: 'Reference Architecture with Terraform: VM-Series in Azure, Centralized Architecture, + Dedicated Inbound NGFW Option' +--- + +# Reference Architecture with Terraform: VM-Series in Azure, Centralized Architecture, Dedicated Inbound NGFW Option + +Palo Alto Networks produces several +[validated reference architecture design and deployment documentation guides](https://www.paloaltonetworks.com/resources/reference-architectures), +which describe well-architected and tested deployments. When deploying VM-Series in a public cloud, the reference architectures +guide users toward the best security outcomes, whilst reducing rollout time and avoiding common integration efforts. + +The Terraform code presented here will deploy Palo Alto Networks VM-Series firewalls in Azure based on a centralized design with +dedicated-inbound VM-Series and integration with Azure Virtual WAN; for a discussion of other options, please see the design guide from +[the reference architecture guides](https://www.paloaltonetworks.com/resources/reference-architectures). + +[![GitHub Logo](/img/view_on_github.png)](https://github.com/PaloAltoNetworks/terraform-azurerm-swfw-modules/tree/main/examples/vmseries_transit_vnet_dedicated_vwan) [![Terraform Logo](/img/view_on_terraform_registry.png)](https://registry.terraform.io/modules/PaloAltoNetworks/swfw-modules/azurerm/latest/examples/vmseries_transit_vnet_dedicated_vwan) + +## Reference Architecture Design + +![simple](aa2ae33a-fb46-4a1c-9811-98ea3b132297.png) + +This code implements: + +- a *centralized design*, a hub-and-spoke topology with a Transit VNet containing VM-Series to inspect all inbound, outbound, + east-west, and enterprise traffic +- the *dedicated inbound option*, which separates inbound traffic flows onto a separate set of VM-Series. + +## Detailed Architecture and Design + +### Centralized Design + +This design uses a Transit VNet. Application functions and resources are deployed across multiple VNets that are connected in +a hub-and-spoke topology. The hub of the topology, or transit VNet, is the central point of connectivity for all inbound, +outbound, east-west, and enterprise traffic. You deploy all VM-Series firewalls within the transit VNet. + +### Dedicated Inbound Option + +The dedicated inbound option separates traffic flows across two separate sets of VM-Series firewalls. One set of VM-Series +firewalls is dedicated to inbound traffic flows, allowing for greater flexibility and scaling of inbound traffic loads. +The second set of VM-Series firewalls services all outbound, east-west, and enterprise network traffic flows. This deployment +choice offers increased scale and operational resiliency and reduces the chances of high bandwidth use from the inbound traffic +flows affecting other traffic flows within the deployment. + +![Detailed Topology Diagram](2ef99143-3dcf-4113-814d-aedca8fcccf3.png) + +This reference architecture consists of: + +- a VNET containing: + - 3 subnets dedicated to the firewalls: management, private and public + - Route Tables and Network Security Groups +- 2 Load Balancers: + - public - with a public IP address assigned, in front of the firewalls public interfaces, for incoming traffic + - private - in front of the firewalls private interfaces, for outgoing and east-west traffic +- a Storage Account used to keep bootstrap packages containing `DAY0` configuration for the firewalls +- 4 firewalls: + - deployed in different zones + - 2 pairs, one for inbound, the other for outbound and east-west traffic + - with 3 network interfaces each: management, public, private + - with public IP addresses assigned to: + - management interface + - public interface +- a Virtual WAN containing: + - one Virtual Hub + - 2 connections to the Virtual Hub (one dedicated for spoke vnet and one dedicated for the transit vnet) +- _(optional)_ test workloads with accompanying infrastructure: + - 3 Spoke VNETs with Route Tables and Network Security Groups + - 3 Spoke VMs serving as WordPress-based web servers + - 3 Azure Bastion managed jump hosts + +**NOTE!** +- In order to deploy the architecture without test workloads described above, empty the `test_infrastructure` map in + `example.tfvars` file. + +## Prerequisites + +A list of requirements might vary depending on the platform used to deploy the infrastructure but a minimum one includes: + +- _(in case of non cloud shell deployment)_ credentials and (optionally) tools required to authenticate against Azure Cloud, see + [AzureRM provider documentation for details](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs#authenticating-to-azure) +- [supported](#requirements) version of [`Terraform`]() +- if you have not run Palo Alto NGFW images in a subscription it might be necessary to accept the license first + ([see this note](../../modules/vmseries#accept-azure-marketplace-terms)) + +**Note!** +- after the deployment the firewalls remain not licensed, they do however contain minimum `DAY0` configuration (required NIC, VR, + routes configuration). +- this example contains some **files** that **can contain sensitive data**. Keep in mind that **this code** is + **only an example**. It's main purpose is to introduce the Terraform modules. + +## Usage + +### Deployment Steps + +- checkout the code locally (if you haven't done so yet) +- copy the [`example.tfvars`](./example.tfvars) file, rename it to `terraform.tfvars` and adjust it to your needs (take a closer + look at the `TODO` markers) +- copy the [`init-cfg.sample.txt`](./files/init-cfg.sample.txt) to `init-cfg.txt` and fill it out with required bootstrap + parameters (see this [documentation](https://docs.paloaltonetworks.com/vm-series/9-1/vm-series-deployment/bootstrap-the-vm-series-firewall/create-the-init-cfgtxt-file/init-cfgtxt-file-components#id07933d91-15be-414d-bc8d-f2a5f3d8df6b) for details) +- _(optional)_ authenticate to AzureRM, switch to the Subscription of your choice +- provide `subscription_id` either by creating an environment variable named `ARM_SUBSCRIPTION_ID` with Subscription ID as value + in your shell (recommended option) or by setting the value of `subscription_id` variable within your `tfvars` file (discouraged + option, we don't recommend putting the Subscription ID in clear text inside the code). +- initialize the Terraform module: + + ```bash + terraform init + ``` + +- _(optional)_ plan you infrastructure to see what will be actually deployed: + + ```bash + terraform plan + ``` + +- deploy the infrastructure (you will have to confirm it with typing in `yes`): + + ```bash + terraform apply + ``` + + The deployment takes couple of minutes. Observe the output. At the end you should see a summary similar to this: + + ```console + bootstrap_storage_urls = + lb_frontend_ips = { + "private" = { + "ha-ports" = "1.2.3.4" + } + "public" = { + "palo-lb-app1-pip" = "1.2.3.4" + } + } + password = + username = "panadmin" + vmseries_mgmt_ips = { + "fw-in-1" = "1.2.3.4" + "fw-in-2" = "1.2.3.4" + "fw-obew-1" = "1.2.3.4" + "fw-obew-2" = "1.2.3.4" + } + ``` + +- at this stage you have to wait couple of minutes for the firewalls to bootstrap. + +### Post deploy + +Firewalls in this example are configured with password authentication. To retrieve the initial credentials run: + +- for username: + + ```bash + terraform output usernames + ``` + +- for password: + + ```bash + terraform output passwords + ``` + +The management public IP addresses are available in the `vmseries_mgmt_ips`: + +```bash +terraform output vmseries_mgmt_ips +``` + +You can now login to the devices using either: + +- cli - ssh client is required +- Web UI (https) - any modern web browser, note that initially the traffic is encrypted with a self-signed certificate. + +As mentioned, the devices already contain `DAY0` configuration, so all network interfaces should be configured and Azure Load +Balancer should already report that the devices are healthy. + +You can now proceed with licensing the devices and configuring your first rules. + +Please also refer to [this repository](https://github.com/PaloAltoNetworks/iron-skillet) for `DAY1` configuration +(security hardening). + +### Cleanup + +To remove the deployed infrastructure run: + +```bash +terraform destroy +``` + +## Reference + +### Requirements + +- `terraform`, version: >= 1.5, < 2.0 + +### Providers + +- `random` +- `local` +- `azurerm` + +### Modules +Name | Version | Source | Description +--- | --- | --- | --- +`vnet` | - | ../../modules/vnet | +`vnet_peering` | - | ../../modules/vnet_peering | +`public_ip` | - | ../../modules/public_ip | +`natgw` | - | ../../modules/natgw | +`load_balancer` | - | ../../modules/loadbalancer | +`appgw` | - | ../../modules/appgw | +`ngfw_metrics` | - | ../../modules/ngfw_metrics | +`bootstrap` | - | ../../modules/bootstrap | +`vmseries` | - | ../../modules/vmseries | +`virtual_wan` | - | ../../modules/vwan | +`test_infrastructure` | - | ../../modules/test_infrastructure | + +### Resources + +- `availability_set` (managed) +- `resource_group` (managed) +- `file` (managed) +- `password` (managed) +- `resource_group` (data) + +### Required Inputs + +Name | Type | Description +--- | --- | --- +[`subscription_id`](#subscription_id) | `string` | Azure Subscription ID is a required argument since AzureRM provider v4. +[`resource_group_name`](#resource_group_name) | `string` | Name of the Resource Group. +[`region`](#region) | `string` | The Azure region to use. +[`vnets`](#vnets) | `map` | A map defining VNETs. + +### Optional Inputs + +Name | Type | Description +--- | --- | --- +[`name_prefix`](#name_prefix) | `string` | A prefix that will be added to all created resources. +[`create_resource_group`](#create_resource_group) | `bool` | When set to `true` it will cause a Resource Group creation. +[`tags`](#tags) | `map` | Map of tags to assign to the created resources. +[`vnet_peerings`](#vnet_peerings) | `map` | A map defining VNET peerings. +[`public_ips`](#public_ips) | `object` | A map defining Public IP Addresses and Prefixes. +[`natgws`](#natgws) | `map` | A map defining NAT Gateways. +[`virtual_wans`](#virtual_wans) | `map` | A map defining Virtual WANs. +[`load_balancers`](#load_balancers) | `map` | A map containing configuration for all (both private and public) Load Balancers. +[`appgws`](#appgws) | `map` | A map defining all Application Gateways in the current deployment. +[`availability_sets`](#availability_sets) | `map` | A map defining availability sets. +[`ngfw_metrics`](#ngfw_metrics) | `object` | A map controlling metrics-relates resources. +[`bootstrap_storages`](#bootstrap_storages) | `map` | A map defining Azure Storage Accounts used to host file shares for bootstrapping NGFWs. +[`vmseries_universal`](#vmseries_universal) | `object` | A map defining common settings for all created VM-Series instances. +[`vmseries`](#vmseries) | `map` | A map defining Azure Virtual Machines based on Palo Alto Networks Next Generation Firewall image. +[`test_infrastructure`](#test_infrastructure) | `map` | A map defining test infrastructure including test VMs and Azure Bastion hosts. + +### Outputs + +Name | Description +--- | --- +`usernames` | Initial administrative username to use for VM-Series. +`passwords` | Initial administrative password to use for VM-Series. +`natgw_public_ips` | Nat Gateways Public IP resources. +`metrics_instrumentation_keys` | The Instrumentation Key of the created instance(s) of Azure Application Insights. +`lb_frontend_ips` | IP Addresses of the load balancers. +`vmseries_mgmt_ips` | IP addresses for the VM-Series management interface. +`bootstrap_storage_urls` | +`test_vms_usernames` | Initial administrative username to use for test VMs. +`test_vms_passwords` | Initial administrative password to use for test VMs. +`test_vms_ips` | IP Addresses of the test VMs. +`test_lb_frontend_ips` | IP Addresses of the test load balancers. + +### Required Inputs details + +#### subscription_id + +Azure Subscription ID is a required argument since AzureRM provider v4. + +**Note!** \ +Instead of putting the Subscription ID directly in the code, it's recommended to use an environment variable. Create an +environment variable named `ARM_SUBSCRIPTION_ID` with your Subscription ID as value and leave this variable set to `null`. + + +Type: string + +[back to list](#modules-required-inputs) + +#### resource_group_name + +Name of the Resource Group. + +Type: string + +[back to list](#modules-required-inputs) + +#### region + +The Azure region to use. + +Type: string + +[back to list](#modules-required-inputs) + +#### vnets + +A map defining VNETs. + +For detailed documentation on each property refer to [module documentation](../../modules/vnet) + +- `create_virtual_network` - (`bool`, optional, defaults to `true`) when set to `true` will create a VNET, `false` will source + an existing VNET. +- `name` - (`string`, required) a name of a VNET. In case `create_virtual_network = false` this should be a + full resource name, including prefixes. +- `resource_group_name` - (`string`, optional, defaults to current RG) a name of an existing Resource Group in which the + VNET will reside or is sourced from. +- `address_space` - (`list`, required when `create_virtual_network = false`) a list of CIDRs for a newly created VNET. +- `dns_servers` - (`list`, optional, defaults to module defaults) a list of IP addresses of custom DNS servers + (by default Azure DNS is used). +- `vnet_encryption` - (`string`, optional, defaults to module default) enables Azure Virtual Network Encryption when + set, only possible value at the moment is `AllowUnencrypted`. When set to `null`, the feature is + disabled. +- `ddos_protection_plan_id` - (`string`, optional, defaults to `null`) ID of an existing Azure Network DDOS Protection Plan to + be associated with the VNET. +- `network_security_groups` - (`map`, optional) map of Network Security Groups to create, for details see + [VNET module documentation](../../modules/vnet#network_security_groups). +- `route_tables` - (`map`, optional) map of Route Tables to create, for details see + [VNET module documentation](../../modules/vnet#route_tables). +- `subnets` - (`map`, optional) map of Subnets to create or source, for details see + [VNET module documentation](../../modules/vnet#subnets). + + +Type: + +```hcl +map(object({ + create_virtual_network = optional(bool, true) + name = string + resource_group_name = optional(string) + address_space = optional(list(string)) + dns_servers = optional(list(string)) + vnet_encryption = optional(string) + ddos_protection_plan_id = optional(string) + network_security_groups = optional(map(object({ + name = string + rules = optional(map(object({ + name = string + priority = number + direction = string + access = string + protocol = string + source_port_range = optional(string) + source_port_ranges = optional(list(string)) + destination_port_range = optional(string) + destination_port_ranges = optional(list(string)) + source_address_prefix = optional(string) + source_address_prefixes = optional(list(string)) + destination_address_prefix = optional(string) + destination_address_prefixes = optional(list(string)) + })), {}) + })), {}) + route_tables = optional(map(object({ + name = string + bgp_route_propagation_enabled = optional(bool) + routes = map(object({ + name = string + address_prefix = string + next_hop_type = string + next_hop_ip_address = optional(string) + })) + })), {}) + subnets = optional(map(object({ + create = optional(bool, true) + name = string + address_prefixes = optional(list(string), []) + network_security_group_key = optional(string) + route_table_key = optional(string) + default_outbound_access_enabled = optional(bool) + enable_storage_service_endpoint = optional(bool) + enable_appgw_delegation = optional(bool) + enable_cloudngfw_delegation = optional(bool) + })), {}) + })) +``` + + +[back to list](#modules-required-inputs) + +### Optional Inputs details + +#### name_prefix + +A prefix that will be added to all created resources. +There is no default delimiter applied between the prefix and the resource name. +Please include the delimiter in the actual prefix. + +Example: +``` +name_prefix = "test-" +``` + +**Note!** \ +This prefix is not applied to existing resources. If you plan to reuse i.e. a VNET please specify it's full name, +even if it is also prefixed with the same value as the one in this property. + + +Type: string + +Default value: `` + +[back to list](#modules-optional-inputs) + +#### create_resource_group + +When set to `true` it will cause a Resource Group creation. +Name of the newly specified RG is controlled by `resource_group_name`. + +When set to `false` the `resource_group_name` parameter is used to specify a name of an existing Resource Group. + + +Type: bool + +Default value: `true` + +[back to list](#modules-optional-inputs) + +#### tags + +Map of tags to assign to the created resources. + +Type: map(string) + +Default value: `map[]` + +[back to list](#modules-optional-inputs) + +#### vnet_peerings + +A map defining VNET peerings. + +Following properties are supported: +- `local_vnet_name` - (`string`, required) name of the local VNET. +- `local_resource_group_name` - (`string`, optional) name of the resource group, in which local VNET exists. +- `remote_vnet_name` - (`string`, required) name of the remote VNET. +- `remote_resource_group_name` - (`string`, optional) name of the resource group, in which remote VNET exists. + + +Type: + +```hcl +map(object({ + local_vnet_name = string + local_resource_group_name = optional(string) + remote_vnet_name = string + remote_resource_group_name = optional(string) + })) +``` + + +Default value: `map[]` + +[back to list](#modules-optional-inputs) + +#### public_ips + +A map defining Public IP Addresses and Prefixes. + +Following properties are available: + +- `public_ip_addresses` - (`map`, optional) map of objects describing Public IP Addresses, please refer to + [module documentation](../../modules/public_ip#public_ip_addresses) + for available properties. +- `public_ip_prefixes` - (`map`, optional) map of objects describing Public IP Prefixes, please refer to + [module documentation](../../modules/public_ip#public_ip_prefixes) + for available properties. + + +Type: + +```hcl +object({ + public_ip_addresses = optional(map(object({ + create = bool + name = string + resource_group_name = optional(string) + zones = optional(list(string)) + domain_name_label = optional(string) + idle_timeout_in_minutes = optional(number) + prefix_name = optional(string) + prefix_resource_group_name = optional(string) + prefix_id = optional(string) + })), {}) + public_ip_prefixes = optional(map(object({ + create = bool + name = string + resource_group_name = optional(string) + zones = optional(list(string)) + length = optional(number) + })), {}) + }) +``` + + +Default value: `map[]` + +[back to list](#modules-optional-inputs) + +#### natgws + +A map defining NAT Gateways. + +Please note that a NAT Gateway is a zonal resource, this means it's always placed in a zone (even when you do not specify one +explicitly). Please refer to Microsoft documentation for notes on NAT Gateway's zonal resiliency. +For detailed documentation on each property refer to [module documentation](../../modules/natgw). + +Following properties are supported: +- `name` - (`string`, required) a name of a NAT Gateway. In case `create_natgw = false` this should be a full + resource name, including prefixes. +- `vnet_key` - (`string`, required) a name (key value) of a VNET defined in `var.vnets` that hosts a subnet this + NAT Gateway will be assigned to. +- `subnet_keys` - (`list(string)`, required) a list of subnets (key values) the NAT Gateway will be assigned to, + defined in `var.vnets` for a VNET described by `vnet_name`. +- `create_natgw` - (`bool`, optional, defaults to `true`) create (`true`) or source an existing NAT Gateway (`false`), + created or sourced: the NAT Gateway will be assigned to a subnet created by the `vnet` module. +- `resource_group_name` - (`string`, optional) name of a Resource Group hosting the NAT Gateway (newly created or the existing + one). +- `zone` - (`string`, optional) an Availability Zone in which the NAT Gateway will be placed, when skipped + Azure will pick a zone. +- `idle_timeout` - (`number`, optional, defults to 4) connection IDLE timeout in minutes, for newly created resources. +- `public_ip` - (`object`, optional) an object defining a public IP resource attached to the NAT Gateway. +- `public_ip_prefix` - (`object`, optional) an object defining a public IP prefix resource attached to the NAT Gatway. + +Example: +``` +natgws = { + "natgw" = { + name = "natgw" + vnet_key = "transit-vnet" + subnet_keys = ["management"] + public_ip = { + create = true + name = "natgw-pip" + } + } +} +``` + + +Type: + +```hcl +map(object({ + name = string + vnet_key = string + subnet_keys = list(string) + create_natgw = optional(bool, true) + resource_group_name = optional(string) + zone = optional(string) + idle_timeout = optional(number, 4) + public_ip = optional(object({ + create = bool + name = optional(string) + resource_group_name = optional(string) + key = optional(string) + })) + public_ip_prefix = optional(object({ + create = bool + name = optional(string) + resource_group_name = optional(string) + length = optional(number) + key = optional(string) + })) + })) +``` + + +Default value: `map[]` + +[back to list](#modules-optional-inputs) + +#### virtual_wans + +A map defining Virtual WANs. + +For detailed documentation on each property refer to [module documentation](../../modules/vwan) + +- `create` - (`bool`, optional, defaults to `true`) when set to `true` will create a new Virtual WAN, + `false` will source an existing Virtual WAN. +- `name` - (`string`, required) a name of a Virtual WAN. In case `create = false` this should be a + full resource name, including prefixes. +- `resource_group_name` - (`string`, optional, defaults to current RG) a name of an existing Resource Group in which + the Virtual WAN will reside or is sourced from. +- `disable_vpn_encryption` - (`bool`, optional, defaults to `false`) if `true`, VPN encryption is disabled. +- `allow_branch_to_branch_traffic` - (`bool`, optional, defaults to `true`) if `false`, branch-to-branch traffic is not allowed. +- `virtual_hubs` - (`map`, optional) map of Virtual Hubs to create or source, for details see + [Virtual WAN module documentation](../../modules/vwan#virtual_hubs). + + +Type: + +```hcl +map(object({ + name = string + resource_group_name = optional(string) + create = optional(bool, true) + region = optional(string) + disable_vpn_encryption = optional(bool, false) + allow_branch_to_branch_traffic = optional(bool, true) + virtual_hubs = optional(map(object({ + name = string + address_prefix = string + create = optional(bool, true) + resource_group_name = optional(string) + region = optional(string) + hub_routing_preference = optional(string) + virtual_router_auto_scale_min_capacity = optional(number) + connections = optional(map(object({ + name = string + connection_type = string + remote_virtual_network_key = optional(string) + internet_security_enabled = optional(bool) + vpn_site_key = optional(string) + vpn_link = optional(list(object({ + vpn_link_name = string + vpn_site_link_key = string + bandwidth_mbps = optional(number) + bgp_enabled = optional(bool) + connection_mode = optional(string) + protocol = optional(string) + ratelimit_enabled = optional(bool) + shared_key = optional(string) + local_azure_ip_address_enabled = optional(bool) + ipsec_policy = optional(object({ + dh_group = optional(string) + ike_encryption_algorithm = optional(string) + ike_integrity_algorithm = optional(string) + encryption_algorithm = optional(string) + integrity_algorithm = optional(string) + pfs_group = optional(string) + sa_data_size_kb = optional(number) + sa_lifetime_sec = optional(number) + })) + }))) + routing = optional(object({ + associated_route_table_key = optional(string) + propagated_route_table_keys = optional(list(string)) + propagated_route_table_labels = optional(set(string)) + static_vnet_route_name = optional(string) + static_vnet_route_address_prefixes = optional(set(string)) + static_vnet_route_next_hop_ip_address = optional(string) + static_vnet_local_route_override_criteria = optional(string) + })) + })), {}) + route_tables = optional(map(object({ + name = string + labels = optional(set(string)) + routes = optional(map(object({ + name = string + destinations_type = string + destinations = list(string) + next_hop_type = optional(string) + next_hop_key = string + })), {}) + })), {}) + routing_intent = optional(object({ + routing_intent_name = string + routing_policy = list(object({ + routing_policy_name = string + destinations = list(string) + next_hop_key = string + })) + })) + vpn_gateway = optional(object({ + name = string + resource_group_name = optional(string) + scale_unit = optional(number) + routing_preference = optional(string) + }), null) + vpn_sites = optional(map(object({ + name = string + region = optional(string) + resource_group_name = optional(string) + address_cidrs = optional(set(string)) + link = optional(map(object({ + name = string + ip_address = optional(string) + fqdn = optional(string) + provider_name = optional(string) + speed_in_mbps = optional(number, 0) + }))) + })), {}) + })), {}) + })) +``` + + +Default value: `map[]` + +[back to list](#modules-optional-inputs) + +#### load_balancers + +A map containing configuration for all (both private and public) Load Balancers. + +This is a brief description of available properties. For a detailed one please refer to +[module documentation](../../modules/loadbalancer). + +Following properties are available: + +- `name` - (`string`, required) a name of the Load Balancer. +- `vnet_key` - (`string`, optional, defaults to `null`) a key pointing to a VNET definition in the `var.vnets` + map that stores the Subnet described by `subnet_key`. +- `zones` - (`list`, optional, defaults to ["1", "2", "3"]) a list of zones for Load Balancer's frontend IP + configurations. +- `backend_name` - (`string`, optional, defaults to "vmseries_backend") a name of the backend pool to create. +- `health_probes` - (`map`, optional, defaults to `null`) a map defining health probes that will be used by load + balancing rules, please refer to + [module documentation](../../modules/loadbalancer#health_probes) for more specific use + cases and available properties. +- `nsg_auto_rules_settings` - (`map`, optional, defaults to `null`) a map defining a location of an existing NSG rule that will + be populated with `Allow` rules for each load balancing rule (`in_rules`), please refer to + [module documentation](../../modules/loadbalancer#nsg_auto_rules_settings) for + available properties. + + Please note that in this example two additional properties are available: + + - `nsg_vnet_key` - (`string`, optional, mutually exclusive with `nsg_name`) a key pointing to a VNET definition in the + `var.vnets` map that stores the NSG described by `nsg_key`. + - `nsg_key` - (`string`, optional, mutually exclusive with `nsg_name`) a key pointing to an NSG definition in the + `var.vnets` map. + +- `frontend_ips` - (`map`, optional, defaults to `{}`) a map containing frontend IP configuration with respective + `in_rules` and `out_rules`, please refer to + [module documentation](../../modules/loadbalancer#frontend_ips) for available + properties. + + **Note!** \ + In this example the `subnet_id` is not available directly, another property has been introduced instead: + + - `subnet_key` - (`string`, optional, defaults to `null`) a key pointing to a Subnet definition in the `var.vnets` map. + + +Type: + +```hcl +map(object({ + name = string + vnet_key = optional(string) + zones = optional(list(string), ["1", "2", "3"]) + backend_name = optional(string, "vmseries_backend") + health_probes = optional(map(object({ + name = string + protocol = string + port = optional(number) + probe_threshold = optional(number) + interval_in_seconds = optional(number) + request_path = optional(string) + }))) + nsg_auto_rules_settings = optional(object({ + nsg_name = optional(string) + nsg_vnet_key = optional(string) + nsg_key = optional(string) + nsg_resource_group_name = optional(string) + source_ips = list(string) + base_priority = optional(number) + })) + frontend_ips = optional(map(object({ + name = string + subnet_key = optional(string) + create_public_ip = optional(bool, false) + public_ip_name = optional(string) + public_ip_resource_group_name = optional(string) + public_ip_key = optional(string) + public_ip_prefix_key = optional(string) + private_ip_address = optional(string) + gwlb_key = optional(string) + in_rules = optional(map(object({ + name = string + protocol = string + port = number + backend_port = optional(number) + health_probe_key = optional(string) + floating_ip = optional(bool) + session_persistence = optional(string) + nsg_priority = optional(number) + idle_timeout_in_minutes = optional(number) + })), {}) + out_rules = optional(map(object({ + name = string + protocol = string + allocated_outbound_ports = optional(number) + enable_tcp_reset = optional(bool) + idle_timeout_in_minutes = optional(number) + })), {}) + })), {}) + })) +``` + + +Default value: `map[]` + +[back to list](#modules-optional-inputs) + +#### appgws + +A map defining all Application Gateways in the current deployment. + +For detailed documentation on how to configure this resource, for available properties, especially for the defaults, +refer to [module documentation](../../modules/appgw). + +**Note!** \ +The `rules` property is meant to bind together `backend_setting`, `redirect` or `url_path_map` (all 3 are mutually exclusive). +It represents the Rules section of an Application Gateway in Azure Portal. + +Below you can find a brief list of most important properties: + +- `name` - (`string`, required) the name of the Application Gateway, will be prefixed with `var.name_prefix`. +- `vnet_key` - (`string`, required) a key pointing to a VNET definition in the `var.vnets` map that stores the Subnet + described by `subnet_key`. +- `subnet_key` - (`string`, required) a key pointing to a Subnet definition in the `var.vnets` map, this has to be an + Application Gateway V2 dedicated subnet. +- `zones` - (`list`, optional, defaults to `["1", "2", "3"]`) parameter controlling if this is a zonal, or a + non-zonal deployment. +- `public_ip` - (`map`, required) defines a Public IP resource used by the Application Gateway instance, a newly created + Public IP will have it's name prefixes with `var.name_prefix`. +- `listeners` - (`map`, required) defines Application Gateway's Listeners, see + [module's documentation](../../modules/appgw#listeners) for details. +- `backend_pool` - (`map`, optional, defaults to module default) backend pool definition, when skipped an empty backend + will be created. +- `backend_settings` - (`map`, optional, mutually exclusive with `redirects` and `url_path_maps`) defines HTTP backend + settings, see [module's documentation](../../modules/appgw#backend_settings) for details. +- `probes` - (`map`, optional, defaults to module default) defines backend probes used check health of backends, see + [module's documentation](../../modules/appgw#probes) for details. +- `rewrites` - (`map`, optional, defaults to module default) defines rewrite rules, see + [module's documentation](../../modules/appgw#rewrites) for details. +- `redirects` - (`map`, optional, mutually exclusive with `backend_settings` and `url_path_maps`) static redirects + definition, see [module's documentation](../../modules/appgw#redirects) for details. +- `url_path_maps` - (`map`, optional, mutually exclusive with `backend_settings` and `redirects`) URL path maps definition, + see [module's documentation](../../modules/appgw#url_path_maps) for details. +- `rules` - (`map`, required) Application Gateway Rules definition, bind together a `listener` with either + `backend_setting`, `redirect` or `url_path_map`, see + [module's documentation](../../modules/appgw#rules) for details. + + +Type: + +```hcl +map(object({ + name = string + vnet_key = string + subnet_key = string + zones = optional(list(string), ["1", "2", "3"]) + public_ip = object({ + create = optional(bool, true) + name = optional(string) + resource_group_name = optional(string) + key = optional(string) + }) + domain_name_label = optional(string) + capacity = optional(object({ + static = optional(number) + autoscale = optional(object({ + min = number + max = number + })) + })) + enable_http2 = optional(bool) + waf = optional(object({ + prevention_mode = bool + rule_set_type = optional(string) + rule_set_version = optional(string) + })) + managed_identities = optional(list(string)) + global_ssl_policy = optional(object({ + type = optional(string) + name = optional(string) + min_protocol_version = optional(string) + cipher_suites = optional(list(string)) + })) + ssl_profiles = optional(map(object({ + name = string + ssl_policy_name = optional(string) + ssl_policy_min_protocol_version = optional(string) + ssl_policy_cipher_suites = optional(list(string)) + }))) + frontend_ip_configuration_name = optional(string, "public_ipconfig") + listeners = map(object({ + name = string + port = number + protocol = optional(string) + host_names = optional(list(string)) + ssl_profile_name = optional(string) + ssl_certificate_path = optional(string) + ssl_certificate_pass = optional(string) + ssl_certificate_vault_id = optional(string) + custom_error_pages = optional(map(string)) + })) + backend_pool = optional(object({ + name = optional(string) + vmseries_ips = optional(list(string)) + })) + backend_settings = optional(map(object({ + name = string + port = number + protocol = string + path = optional(string) + hostname_from_backend = optional(string) + hostname = optional(string) + timeout = optional(number) + use_cookie_based_affinity = optional(bool) + affinity_cookie_name = optional(string) + probe_key = optional(string) + root_certs = optional(map(object({ + name = string + path = string + }))) + }))) + probes = optional(map(object({ + name = string + path = string + host = optional(string) + port = optional(number) + protocol = optional(string) + interval = optional(number) + timeout = optional(number) + threshold = optional(number) + match_code = optional(list(number)) + match_body = optional(string) + }))) + rewrites = optional(map(object({ + name = optional(string) + rules = optional(map(object({ + name = string + sequence = number + conditions = optional(map(object({ + pattern = string + ignore_case = optional(bool) + negate = optional(bool) + }))) + request_headers = optional(map(string)) + response_headers = optional(map(string)) + }))) + }))) + redirects = optional(map(object({ + name = string + type = string + target_listener_key = optional(string) + target_url = optional(string) + include_path = optional(bool) + include_query_string = optional(bool) + }))) + url_path_maps = optional(map(object({ + name = string + backend_key = string + path_rules = optional(map(object({ + paths = list(string) + backend_key = optional(string) + redirect_key = optional(string) + }))) + }))) + rules = map(object({ + name = string + priority = number + backend_key = optional(string) + listener_key = string + rewrite_key = optional(string) + url_path_map_key = optional(string) + redirect_key = optional(string) + })) + })) +``` + + +Default value: `map[]` + +[back to list](#modules-optional-inputs) + +#### availability_sets + +A map defining availability sets. Can be used to provide infrastructure high availability when zones cannot be used. + +Following properties are supported: + +- `name` - (`string`, required) name of the Application Insights. +- `update_domain_count` - (`number`, optional, defaults to Azure default) specifies the number of update domains that are used. +- `fault_domain_count` - (`number`, optional, defaults to Azure default) specifies the number of fault domains that are used. + +**Note!** \ +Please keep in mind that Azure defaults are not working for every region (especially the small ones, without any Availability +Zones). Please verify how many update and fault domain are supported in a region before deploying this resource. + + +Type: + +```hcl +map(object({ + name = string + update_domain_count = optional(number) + fault_domain_count = optional(number) + })) +``` + + +Default value: `map[]` + +[back to list](#modules-optional-inputs) + +#### ngfw_metrics + +A map controlling metrics-relates resources. + +When set to explicit `null` (default) it will disable any metrics resources in this deployment. + +When defined it will either create or source a Log Analytics Workspace and create Application Insights instances (one per each +Scale Set). All instances will be automatically connected to the workspace. The name of the Application Insights instance will +be derived from the Scale Set name and suffixed with `-ai`. + +All the settings available below are common to the Log Analytics Workspace and Application Insight instances. + +Following properties are available: + +- `name` - (`string`, required) name of the (common) Log Analytics Workspace. +- `create_workspace` - (`bool`, optional, defaults to `true`) controls whether we create or source an existing Log + Analytics Workspace. +- `resource_group_name` - (`string`, optional, defaults to `var.resource_group_name`) name of the Resource Group hosting + the Log Analytics Workspace. +- `sku` - (`string`, optional, defaults to module default) the SKU of the Log Analytics Workspace. +- `metrics_retention_in_days` - (`number`, optional, defaults to module default) workspace and insights data retention in days, + possible values are between 30 and 730. For sourced Workspaces this applies only to the + Application Insights instances. + + +Type: + +```hcl +object({ + name = string + create_workspace = optional(bool, true) + resource_group_name = optional(string) + sku = optional(string) + metrics_retention_in_days = optional(number) + }) +``` + + +Default value: `&{}` + +[back to list](#modules-optional-inputs) + +#### bootstrap_storages + +A map defining Azure Storage Accounts used to host file shares for bootstrapping NGFWs. + +You can create or re-use an existing Storage Account and/or File Share. For details on all available properties please refer to +[module's documentation](../../modules/bootstrap). Following is just an extract of the most important ones: + +- `name` - (`string`, required) name of the Storage Account that will be created or sourced. + + **Note** \ + For new Storage Accounts this name will not be prefixed with `var.name_prefix`. \ + Please note the limitations on naming. This has to be a globally unique name, between 3 and 63 chars, only lower-case letters + and numbers. + +- `resource_group_name` - (`string`, optional, defaults to `null`) name of the Resource Group that hosts (sourced) or + will host (created) a Storage Account. When skipped the code will fall back to + `var.resource_group_name`. +- `storage_account` - (`map`, optional, defaults to `{}`) a map controlling basic Storage Account configuration. + + The property you should pay attention to is: + + - `create` - (`bool`, optional, defaults to module default) controls if the Storage Account specified in the `name` property + will be created or sourced. + + For detailed documentation see [module's documentation](../../modules/bootstrap#storage_account). + +- `storage_network_security` - (`map`, optional, defaults to `{}`) a map defining network security settings for a **new** + storage account. + + The properties you should pay attention to are: + + - `allowed_subnet_keys` - (`list`, optional, defaults to `[]`) a list of keys pointing to Subnet definitions in the + `var.vnets` map. These Subnets will have dedicated access to the Storage Account. For this to work + they also need to have the Storage Account Service Endpoint enabled. + - `vnet_key` - (`string`, optional) a key pointing to a VNET definition in the `var.vnets` map that stores the + Subnets described in `allowed_subnet_keys`. + + For detailed documentation see [module's documentation](../../modules/bootstrap#storage_network_security). + +- `file_shares_configuration` - (`map`, optional, defaults to `{}`) a map defining common File Share setting. + + The properties you should pay attention to are: + + - `create_file_shares` - (`bool`, optional, defaults to module default) controls if the File Shares defined in the + `file_shares` property will be created or sourced. + - `disable_package_dirs_creation` - (`bool`, optional, defaults to module default) for sourced File Shares, controls if the + bootstrap package folder structure will be created. + + For detailed documentation see [module's documentation](../../modules/bootstrap#file_shares_configuration). + +- `file_shares` - (`map`, optional, defaults to `{}`) a map that holds File Shares and bootstrap package + configuration. For detailed description see + [module's documentation](../../modules/bootstrap#file_shares). + + +Type: + +```hcl +map(object({ + name = string + resource_group_name = optional(string) + storage_account = optional(object({ + create = optional(bool) + replication_type = optional(string) + kind = optional(string) + tier = optional(string) + blob_retention = optional(number) + }), {}) + storage_network_security = optional(object({ + min_tls_version = optional(string) + allowed_public_ips = optional(list(string)) + vnet_key = optional(string) + allowed_subnet_keys = optional(list(string), []) + }), {}) + file_shares_configuration = optional(object({ + create_file_shares = optional(bool) + disable_package_dirs_creation = optional(bool) + quota = optional(number) + access_tier = optional(string) + }), {}) + file_shares = optional(map(object({ + name = string + bootstrap_package_path = optional(string) + bootstrap_files = optional(map(string)) + bootstrap_files_md5 = optional(map(string)) + quota = optional(number) + access_tier = optional(string) + })), {}) + })) +``` + + +Default value: `map[]` + +[back to list](#modules-optional-inputs) + +#### vmseries_universal + +A map defining common settings for all created VM-Series instances. + +It duplicates popular properties from `vmseries` variable, specifically `vmseries.image` and `vmseries.virtual_machine` maps. +However, if values are set in those maps, they still take precedence over the ones set within this variable. As a result, all +universal properties can be overriden on a per-VM basis. + +Following properties are supported: + +- `use_airs` - (`bool`, optional, defaults to `false`) when set to `true`, the AI Runtime Security VM image is used + instead of the one passed to the module and version for `airs-flex` offer must be provided. +- `version` - (`string`, optional) describes the PAN-OS image version from Azure Marketplace. +- `size` - (`string`, optional, defaults to module default) Azure VM size (type). Consult the *VM-Series + Deployment Guide* as only a few selected sizes are supported. +- `bootstrap_options` - (`map`, optional, mutually exclusive with `bootstrap_package`) bootstrap options passed to PAN-OS + when launched for the 1st time, for details see module documentation. +- `bootstrap_package` - (`map`, optional, mutually exclusive with `bootstrap_options`) a map defining content of the bootstrap + package. For details and available properties refer to `vmseries` variable. + + +Type: + +```hcl +object({ + use_airs = optional(bool) + version = optional(string) + size = optional(string) + bootstrap_options = optional(object({ + type = optional(string) + ip-address = optional(string) + default-gateway = optional(string) + netmask = optional(string) + ipv6-address = optional(string) + ipv6-default-gateway = optional(string) + hostname = optional(string) + panorama-server = optional(string) + panorama-server-2 = optional(string) + tplname = optional(string) + dgname = optional(string) + cgname = optional(string) + dns-primary = optional(string) + dns-secondary = optional(string) + vm-auth-key = optional(string) + op-command-modes = optional(string) + op-cmd-dpdk-pkt-io = optional(string) + plugin-op-commands = optional(string) + dhcp-send-hostname = optional(string) + dhcp-send-client-id = optional(string) + dhcp-accept-server-hostname = optional(string) + dhcp-accept-server-domain = optional(string) + vm-series-auto-registration-pin-id = optional(string) + vm-series-auto-registration-pin-value = optional(string) + auth-key = optional(string) + authcodes = optional(string) + })) + bootstrap_package = optional(object({ + bootstrap_storage_key = string + static_files = optional(map(string), {}) + bootstrap_package_path = optional(string) + bootstrap_xml_template = optional(string) + private_snet_key = optional(string) + public_snet_key = optional(string) + ai_update_interval = optional(number, 5) + intranet_cidr = optional(string) + })) + }) +``` + + +Default value: `map[]` + +[back to list](#modules-optional-inputs) + +#### vmseries + +A map defining Azure Virtual Machines based on Palo Alto Networks Next Generation Firewall image. + +For details and defaults for available options please refer to the [`vmseries`](../../modules/vmseries) module. + +The most basic properties are as follows: + +- `name` - (`string`, required) name of the VM, will be prefixed with the value of `var.name_prefix`. +- `vnet_key` - (`string`, required) a key of a VNET defined in `var.vnets`. This is the VNET that hosts subnets used to + deploy network interfaces for deployed VM. +- `authentication` - (`map`, optional, defaults to example defaults) authentication settings for the deployed VM. + + The `authentication` property is optional and holds the firewall admin access details. By default, standard username + `panadmin` will be set and a random password will be auto-generated for you (available in Terraform outputs). + + **Note!** \ + The `disable_password_authentication` property is by default `false` in this example. When using this value, you don't have + to specify anything but you can still additionally pass SSH keys for authentication. You can however set this property to + `true`, then you have to specify `ssh_keys` property. + + For all properties and their default values see [module's documentation](../../modules/vmseries#authentication). + +- `image` - (`map`, optional) properties defining a base image used by the deployed VM. The `image` property is + required (if no common properties were set within `vmseries_universal` variable) but there are only 2 + properties (mutually exclusive) that have to be set, either: + + - `version` - (`string`, optional) describes the PAN-OS image version from Azure Marketplace. + - `custom_id` - (`string`, optional) absolute ID of your own custom PAN-OS image. + + For details on all properties refer to [module's documentation](../../modules/vmseries#image). + +- `virtual_machine` - (`map`, optional, defaults to module default) a map that groups most common VM configuration options. + Most common properties are: + + - `size` - (`string`, optional, defaults to module default) Azure VM size (type). Consult the *VM-Series + Deployment Guide* as only a few selected sizes are supported. + - `zone` - (`string`, required) the Availability Zone in which the VM and (if deployed) public IP addresses will + be created. + - `disk_type` - (`string`, optional, defaults to module default) type of a Managed Disk which should be created, + possible values are `Standard_LRS`, `StandardSSD_LRS` or `Premium_LRS` (works only for selected + `size` values). + - `bootstrap_options` - (`map`, optional, mutually exclusive with `bootstrap_package`) bootstrap options passed to PAN-OS + when launched for the 1st time, for details see module documentation. + - `bootstrap_package` - (`map`, optional, mutually exclusive with `bootstrap_options`) a map defining content of the + bootstrap package. + + **Note!** \ + At least one of `static_files`, `bootstrap_xml_template` or `bootstrap_package_path` is required. You can use a combination + of all 3. The `bootstrap_package_path` is the less important. For details on this mechanism and for details on the other + properties see the [`bootstrap` module documentation](../../modules/bootstrap). + + Following properties are available: + + - `bootstrap_storage_key` - (`string`, required) a key of a bootstrap storage defined in `var.bootstrap_storages` that + will host bootstrap packages. Each package will be hosted on a separate File Share. The File + Shares will be created automatically, one for each firewall. + - `static_files` - (`map`, optional, defaults to `{}`) a map containing files that will be copied to a File + Share, see [`file_shares.bootstrap_files`](../../modules/bootstrap#file_shares) + property documentation for details. + - `bootstrap_package_path` - (`string`, optional, defaults to `null`) a path to a folder containing a full bootstrap + package. + - `bootstrap_xml_template` - (`string`, optional, defaults to `null`) a path to a `bootstrap.xml` template. If this example + is using full bootstrap method, the sample templates are in [`templates`](./templates) folder. + + The templates are used to provide `day0` like configuration which consists of: + + - network interfaces configuration. + - one or more (depending on the architecture) Virtual Routers configurations. This config contains static routes + required for the Load Balancer (and Application Gateway, if defined) health checks to work and routes that allow + Inbound and OBEW traffic. + - *any-any* security rule. + - an outbound NAT rule that will allow the Outbound traffic to flow to the Internet. + + **Note!** \ + Day0 configuration is **not meant** to be **secure**. It's here merely to help with the basic firewall setup. When + `bootstrap_xml_template` is set, one of the following properties might be required. + + - `private_snet_key` - (`string`, required only when `bootstrap_xml_template` is set, defaults to `null`) a key + pointing to a private Subnet definition in `var.vnets` (the `vnet_key` property is used to + identify a VNET). The Subnet definition is used to calculate static routes for a private + Load Balancer health checks and for Inbound traffic. + - `public_snet_key` - (`string`, required only when `bootstrap_xml_template` is set, defaults to `null`) a key + pointing to a public Subnet definition in `var.vnets` (the `vnet_key` property is used to + identify a VNET). The Subnet definition is used to calculate static routes for a public + Load Balancer health checks and for Outbound traffic. + - `ai_update_interval` - (`number`, optional, defaults to `5`) Application Insights update interval, used only when + `ngfw_metrics` module is defined and used in this example. The Application Insights + Instrumentation Key will be populated automatically. + - `intranet_cidr` - (`string`, optional, defaults to `null`) a CIDR of the Intranet - combined CIDR of all + private networks. When set it will override the private Subnet CIDR for inbound traffic + static routes. + + For details on all properties refer to [module's documentation](../../modules/vmseries#virtual_machine). + +- `interfaces` - (`list`, required) configuration of all network interfaces. Order of the interfaces does matter - the + 1st interface is the management one. Most common properties are: + + - `name` - (`string`, required) name of the network interface (will be prefixed with `var.name_prefix`). + - `subnet_key` - (`string`, required) a key of a subnet to which the interface will be assigned as defined in + `var.vnets`. Key identifying the VNET is defined in `virtual_machine.vnet_key` property. + - `ip_configurations` - (`map`, required) A map that contains the IP configurations for the interface. + - `name` - (`string`, optional, defaults to `primary`) the name of the interface IP configuration. + - `primary` - (`bool`, optional, defaults to `true`) sets the current IP configuration as the primary one. + **Note!** When you define multiple IP configurations, exactly one must be the primary. + - `private_ip_address` - (`string`, optional, defaults to `null`) static private IP to assign to the interface. When + skipped Azure will assign one dynamically. Keep in mind that a dynamic IP is guarantied not + to change as long as the VM is running. Any stop/deallocate/restart operation might cause + the IP to change. + - `create_public_ip` - (`bool`, optional, defaults to `false`) if `true`, creates a public IP for the interface. + - `load_balancer_key` - (`string`, optional, defaults to `null`) key of a Load Balancer defined in `var.loadbalancers` + variable, network interface that has this property defined will be added to the Load Balancer's + backend pool. + - `application_gateway_key` - (`string`, optional, defaults to `null`) key of an Application Gateway defined in `var.appgws` + variable, network interface that has this property defined will be added to the Application + Gateway's backend pool. Mutually exclusive with `appgw_backend_pool_id`. + - `appgw_backend_pool_id` - (`string`, optional, defaults to `null`) ID of the Application Gateway backend pool to which + the network interface will be added. Mutually exclusive with `application_gateway_key`. + + For details on all properties refer to [module's documentation](../../modules/panorama#interfaces). + + +Type: + +```hcl +map(object({ + name = string + vnet_key = string + authentication = optional(object({ + username = optional(string, "panadmin") + password = optional(string) + disable_password_authentication = optional(bool, false) + ssh_keys = optional(list(string), []) + }), {}) + image = optional(object({ + use_airs = optional(bool) + version = optional(string) + publisher = optional(string) + offer = optional(string) + sku = optional(string) + enable_marketplace_plan = optional(bool) + custom_id = optional(string) + })) + virtual_machine = object({ + size = optional(string) + bootstrap_options = optional(object({ + type = optional(string) + ip-address = optional(string) + default-gateway = optional(string) + netmask = optional(string) + ipv6-address = optional(string) + ipv6-default-gateway = optional(string) + hostname = optional(string) + panorama-server = optional(string) + panorama-server-2 = optional(string) + tplname = optional(string) + dgname = optional(string) + cgname = optional(string) + dns-primary = optional(string) + dns-secondary = optional(string) + vm-auth-key = optional(string) + op-command-modes = optional(string) + op-cmd-dpdk-pkt-io = optional(string) + plugin-op-commands = optional(string) + dhcp-send-hostname = optional(string) + dhcp-send-client-id = optional(string) + dhcp-accept-server-hostname = optional(string) + dhcp-accept-server-domain = optional(string) + vm-series-auto-registration-pin-id = optional(string) + vm-series-auto-registration-pin-value = optional(string) + auth-key = optional(string) + authcodes = optional(string) + })) + bootstrap_package = optional(object({ + bootstrap_storage_key = string + static_files = optional(map(string), {}) + bootstrap_package_path = optional(string) + bootstrap_xml_template = optional(string) + private_snet_key = optional(string) + public_snet_key = optional(string) + ai_update_interval = optional(number, 5) + intranet_cidr = optional(string) + })) + zone = string + disk_type = optional(string) + disk_name = optional(string) + avset_key = optional(string) + capacity_reservation_group_id = optional(string) + accelerated_networking = optional(bool) + allow_extension_operations = optional(bool) + encryption_at_host_enabled = optional(bool) + disk_encryption_set_id = optional(string) + enable_boot_diagnostics = optional(bool, true) + boot_diagnostics_storage_uri = optional(string) + identity_type = optional(string) + identity_ids = optional(list(string)) + }) + interfaces = list(object({ + name = string + subnet_key = string + ip_configurations = map(object({ + name = optional(string) + primary = optional(bool, true) + create_public_ip = optional(bool, false) + public_ip_name = optional(string) + public_ip_resource_group_name = optional(string) + public_ip_key = optional(string) + private_ip_address = optional(string) + })) + load_balancer_key = optional(string) + application_gateway_key = optional(string) + appgw_backend_pool_id = optional(string) + })) + })) +``` + + +Default value: `map[]` + +[back to list](#modules-optional-inputs) + +#### test_infrastructure + +A map defining test infrastructure including test VMs and Azure Bastion hosts. + +For details and defaults for available options please refer to the +[`test_infrastructure`](../../modules/test_infrastructure) module. + +Following properties are supported: + +- `create_resource_group` - (`bool`, optional, defaults to `true`) when set to `true`, a new Resource Group is created. When + set to `false`, an existing Resource Group is sourced. +- `resource_group_name` - (`string`, optional) name of the Resource Group to be created or sourced. +- `vnets` - (`map`, required) a map defining VNETs and peerings for the test environment. The most basic + properties are as follows: + + - `create_virtual_network` - (`bool`, optional, defaults to `true`) when set to `true` will create a VNET, + `false` will source an existing VNET. + - `name` - (`string`, required) a name of a VNET. In case `create_virtual_network = `false` this should be + a full resource name, including prefixes. + - `address_space` - (`list(string)`, required when `create_virtual_network = `false`) a list of CIDRs for a newly + created VNET. + - `network_security_groups` - (`map`, optional) map of Network Security Groups to create, for details see + [VNET module documentation](../../modules/vnet#network_security_groups). + - `route_tables` - (`map`, optional) map of Route Tables to create, for details see + [VNET module documentation](../../modules/vnet#route_tables). + - `subnets` - (`map`, optional) map of Subnets to create or source, for details see + [VNET module documentation](../../modules/vnet#subnets). + - `local_peer_config` - (`map`, optional) a map that contains local peer configuration parameters. This value allows to + set `allow_virtual_network_access`, `allow_forwarded_traffic`, `allow_gateway_transit` and + `use_remote_gateways` parameters on the local VNet peering. + - `remote_peer_config` - (`map`, optional) a map that contains remote peer configuration parameters. This value allows to + set `allow_virtual_network_access`, `allow_forwarded_traffic`, `allow_gateway_transit` and + `use_remote_gateways` parameters on the remote VNet peering. + + For all properties and their default values see [module's documentation](../../modules/test_infrastructure#vnets). + +- `load_balancers` - (`map`, optional) a map containing configuration for all (both private and public) Load Balancers. + The most basic properties are as follows: + + - `name` - (`string`, required) a name of the Load Balancer. + - `vnet_key` - (`string`, optional, defaults to `null`) a key pointing to a VNET definition in the `var.vnets` + map that stores the Subnet described by `subnet_key`. + - `zones` - (`list`, optional, defaults to ["1", "2", "3"]) a list of zones for Load Balancer's frontend IP + configurations. + - `backend_name` - (`string`, optional) a name of the backend pool to create. + - `health_probes` - (`map`, optional, defaults to `null`) a map defining health probes that will be used by load + balancing rules, please refer to + [loadbalancer module documentation](../../modules/loadbalancer#health_probes) for + more specific use cases and available properties. + - `nsg_auto_rules_settings` - (`map`, optional, defaults to `null`) a map defining a location of an existing NSG rule that + will be populated with `Allow` rules for each load balancing rule (`in_rules`), please refer to + [loadbalancer module documentation](../../modules/loadbalancer#nsg_auto_rules_settings) + for available properties. + + Please note that in this example two additional properties are available: + + - `nsg_vnet_key` - (`string`, optional, mutually exclusive with `nsg_name`) a key pointing to a VNET definition in the + `var.vnets` map that stores the NSG described by `nsg_key`. + - `nsg_key` - (`string`, optional, mutually exclusive with `nsg_name`) a key pointing to an NSG definition in the + `var.vnets` map. + + - `frontend_ips` - (`map`, optional, defaults to `{}`) a map containing frontend IP configuration with respective + `in_rules` and `out_rules`, please refer to + [loadbalancer module documentation](../../modules/loadbalancer#frontend_ips) for + available properties. + + **Note!** \ + In this example the `subnet_id` is not available directly, another property has been introduced instead: + + - `subnet_key` - (`string`, optional, defaults to `null`) a key pointing to a Subnet definition in the `var.vnets` map. + + For all properties and their default values see + [module's documentation](../../modules/test_infrastructure#load_balancers). + +- `authentication` - (`map`, optional, defaults to example defaults) authentication settings for the deployed VMs. +- `spoke_vms` - (`map`, required) a map defining test VMs. The most basic properties are as follows: + + - `name` - (`string`, required) a name of the VM. + - `vnet_key` - (`string`, required) a key describing a VNET defined in `vnets` property. + - `subnet_key` - (`string`, required) a key describing a Subnet found in a VNET definition. + - `load_balancer_key` - (`string`, optional) a key describing a Load Balancer defined in `load_balancers` property. + + For all properties and their default values see + [module's documentation](../../modules/test_infrastructure#test_vms). + +- `bastions` - (`map`, required) a map containing Azure Bastion definitions. The most basic properties are as + follows: + + - `name` - (`string`, required) an Azure Bastion name. + - `vnet_key` - (`string`, required) a key describing a VNET defined in `vnets` property. This VNET should already have an + existing subnet called `AzureBastionSubnet` (the name is hardcoded by Microsoft). + - `subnet_key` - (`string`, required) a key pointing to a Subnet dedicated to a Bastion deployment. + + For all properties and their default values see + [module's documentation](../../modules/test_infrastructure#bastions). + + +Type: + +```hcl +map(object({ + create_resource_group = optional(bool, true) + resource_group_name = optional(string) + vnets = map(object({ + create_virtual_network = optional(bool, true) + name = string + address_space = optional(list(string)) + dns_servers = optional(list(string)) + vnet_encryption = optional(string) + ddos_protection_plan_id = optional(string) + hub_vnet_key = optional(string) + network_security_groups = optional(map(object({ + name = string + rules = optional(map(object({ + name = string + priority = number + direction = string + access = string + protocol = string + source_port_range = optional(string) + source_port_ranges = optional(list(string)) + destination_port_range = optional(string) + destination_port_ranges = optional(list(string)) + source_address_prefix = optional(string) + source_address_prefixes = optional(list(string)) + destination_address_prefix = optional(string) + destination_address_prefixes = optional(list(string)) + })), {}) + })), {}) + route_tables = optional(map(object({ + name = string + bgp_route_propagation_enabled = optional(bool) + routes = map(object({ + name = string + address_prefix = string + next_hop_type = string + next_hop_ip_address = optional(string) + })) + })), {}) + subnets = optional(map(object({ + create = optional(bool, true) + name = string + address_prefixes = optional(list(string), []) + network_security_group_key = optional(string) + route_table_key = optional(string) + default_outbound_access_enabled = optional(bool) + enable_storage_service_endpoint = optional(bool) + enable_appgw_delegation = optional(bool) + enable_cloudngfw_delegation = optional(bool) + })), {}) + local_peer_config = optional(object({ + allow_virtual_network_access = optional(bool, true) + allow_forwarded_traffic = optional(bool, true) + allow_gateway_transit = optional(bool, false) + use_remote_gateways = optional(bool, false) + }), {}) + remote_peer_config = optional(object({ + allow_virtual_network_access = optional(bool, true) + allow_forwarded_traffic = optional(bool, true) + allow_gateway_transit = optional(bool, false) + use_remote_gateways = optional(bool, false) + }), {}) + })) + load_balancers = optional(map(object({ + name = string + vnet_key = optional(string) + zones = optional(list(string), ["1", "2", "3"]) + backend_name = optional(string) + health_probes = optional(map(object({ + name = string + protocol = string + port = optional(number) + probe_threshold = optional(number) + interval_in_seconds = optional(number) + request_path = optional(string) + }))) + nsg_auto_rules_settings = optional(object({ + nsg_name = optional(string) + nsg_vnet_key = optional(string) + nsg_key = optional(string) + nsg_resource_group_name = optional(string) + source_ips = list(string) + base_priority = optional(number) + })) + frontend_ips = optional(map(object({ + name = string + subnet_key = optional(string) + create_public_ip = optional(bool, false) + public_ip_name = optional(string) + public_ip_resource_group_name = optional(string) + public_ip_key = optional(string) + public_ip_prefix_key = optional(string) + private_ip_address = optional(string) + gwlb_key = optional(string) + in_rules = optional(map(object({ + name = string + protocol = string + port = number + backend_port = optional(number) + health_probe_key = optional(string) + floating_ip = optional(bool) + session_persistence = optional(string) + nsg_priority = optional(number) + idle_timeout_in_minutes = optional(number) + })), {}) + out_rules = optional(map(object({ + name = string + protocol = string + allocated_outbound_ports = optional(number) + enable_tcp_reset = optional(bool) + idle_timeout_in_minutes = optional(number) + })), {}) + })), {}) + })), {}) + authentication = optional(object({ + username = optional(string, "bitnami") + password = optional(string) + }), {}) + spoke_vms = map(object({ + name = string + interface_name = optional(string) + disk_name = optional(string) + vnet_key = string + subnet_key = string + load_balancer_key = optional(string) + private_ip_address = optional(string) + size = optional(string) + image = optional(object({ + publisher = optional(string) + offer = optional(string) + sku = optional(string) + version = optional(string) + enable_marketplace_plan = optional(bool) + }), {}) + custom_data = optional(string) + })) + bastions = map(object({ + name = string + create_public_ip = optional(bool, true) + public_ip_name = optional(string) + public_ip_resource_group_name = optional(string) + public_ip_key = optional(string) + vnet_key = string + subnet_key = string + })) + })) +``` + + +Default value: `map[]` + +[back to list](#modules-optional-inputs) \ No newline at end of file