palo-alto-ha tf examples #5

Closed
michael.sodan wants to merge 2 commits from palo into main
13 changed files with 481 additions and 0 deletions

View file

@ -0,0 +1,23 @@
terraform {
required_providers {
stackit = {
source = "stackitcloud/stackit"
version = ">=0.50.0"
}
}
}
# Authentication
# Key flow (using path)
provider "stackit" {
default_region = var.default_region
service_account_key_path = var.service_account_key_path
enable_beta_resources = true
}
module "project" {
source = "./project" # Der Pfad zum Modul-Verzeichnis
}

View file

@ -0,0 +1,158 @@
resource "stackit_network" "mgmt_network" {
project_id = module.project.project_info["project_id"]
name = "mgmt_network"
ipv4_nameservers = ["1.1.1.1", "8.8.8.8"]
ipv4_prefix = "10.220.129.0/24"
routed = true
}
resource "stackit_network" "ha_network" {
project_id = module.project.project_info["project_id"]
name = "ha_network"
ipv4_prefix = "10.220.254.0/24"
routed = false
}
resource "stackit_network" "wan_network" {
project_id = module.project.project_info["project_id"]
name = "wan_network"
ipv4_nameservers = ["1.1.1.1", "8.8.8.8"]
ipv4_prefix = "10.220.131.0/24"
routed = true
}
resource "stackit_network" "lan_network1" {
project_id = module.project.project_info["project_id"]
name = "lan_network1"
ipv4_prefix = "10.220.1.0/24"
routed = false
}
resource "stackit_network" "lan_network2" {
project_id = module.project.project_info["project_id"]
name = "lan_network2"
ipv4_prefix = "10.220.2.0/24"
routed = false
}
resource "stackit_network_interface" "mgmt" {
project_id = module.project.project_info["project_id"]
network_id = stackit_network.mgmt_network.network_id
security = false
name = "MGMT"
ipv4 = "10.220.129.17"
}
resource "stackit_network_interface" "ha" {
project_id = module.project.project_info["project_id"]
network_id = stackit_network.ha_network.network_id
security = false
name = "HA"
ipv4 = "10.220.254.100"
}
resource "stackit_network_interface" "wan" {
project_id = module.project.project_info["project_id"]
network_id = stackit_network.wan_network.network_id
security = true
name = "MGMT"
ipv4 = "10.220.131.10"
allowed_addresses = ["10.220.131.30/32", "0.0.0.0/0"]
security_group_ids = [stackit_security_group.paloalto.security_group_id]
}
resource "stackit_network_interface" "mgmt2" {
project_id = module.project.project_info["project_id"]
network_id = stackit_network.mgmt_network.network_id
security = false
name = "MGMT2"
ipv4 = "10.220.129.18"
}
resource "stackit_network_interface" "ha2" {
project_id = module.project.project_info["project_id"]
network_id = stackit_network.ha_network.network_id
security = false
name = "HA2"
ipv4 = "10.220.254.200"
}
resource "stackit_network_interface" "wan2" {
project_id = module.project.project_info["project_id"]
network_id = stackit_network.wan_network.network_id
security = true
name = "WAN2"
allowed_addresses = ["10.220.131.30/32", "0.0.0.0/0"]
security_group_ids = [stackit_security_group.paloalto.security_group_id]
}
resource "stackit_network_interface" "vip" {
project_id = module.project.project_info["project_id"]
network_id = stackit_network.wan_network.network_id
security = false
name = "VIP"
ipv4 = "10.220.131.30"
}
resource "stackit_network_interface" "lan1" {
project_id = module.project.project_info["project_id"]
network_id = stackit_network.lan_network1.network_id
security = false
name = "LAN1"
}
resource "stackit_network_interface" "lan1_2" {
project_id = module.project.project_info["project_id"]
network_id = stackit_network.lan_network1.network_id
security = false
name = "LAN1"
}
resource "stackit_network_interface" "lan2" {
project_id = module.project.project_info["project_id"]
network_id = stackit_network.lan_network2.network_id
security = false
name = "LAN2"
}
resource "stackit_network_interface" "lan2_2" {
project_id = module.project.project_info["project_id"]
network_id = stackit_network.lan_network2.network_id
security = false
name = "LAN2"
}
resource "stackit_public_ip" "mgmt_ip" {
project_id = module.project.project_info["project_id"]
network_interface_id = stackit_network_interface.mgmt.network_interface_id
}
resource "stackit_public_ip" "wan_ip" {
project_id = module.project.project_info["project_id"]
network_interface_id = stackit_network_interface.wan.network_interface_id
}
resource "stackit_public_ip" "mgmt2_ip" {
project_id = module.project.project_info["project_id"]
network_interface_id = stackit_network_interface.mgmt2.network_interface_id
}
resource "stackit_public_ip" "wan2_ip" {
project_id = module.project.project_info["project_id"]
network_interface_id = stackit_network_interface.wan2.network_interface_id
}
resource "stackit_public_ip" "vip_ip" {
project_id = module.project.project_info["project_id"]
network_interface_id = stackit_network_interface.vip.network_interface_id
}
output "public_ips" {
value = {
"mgmt_ip" = stackit_public_ip.mgmt_ip.ip
"wan_ip" = stackit_public_ip.wan_ip.ip
"mgmt2_ip" = stackit_public_ip.mgmt2_ip.ip
"wan2_ip" = stackit_public_ip.wan2_ip.ip
"VIP" = stackit_public_ip.vip_ip.ip
}
}

View file

@ -0,0 +1,13 @@
// Upload VPN Appliance Image to STACKIT
resource "stackit_image" "paloalto" {
project_id = module.project.project_info["project_id"]
name = "PA-VM-KVM-11.2.5-root"
local_file_path = "./PA-VM-KVM-11.2.5.qcow2"
disk_format = "qcow2"
min_disk_size = 80
min_ram = 8
config = {
uefi = false
}
}

View file

@ -0,0 +1,48 @@
resource "stackit_volume" "paloalto_vol" {
project_id = module.project.project_info["project_id"]
name = "PA-VM-KVM-11.2.5-root"
availability_zone = var.region_az1
size = 100
performance_class = "storage_premium_perf2"
source = {
id = stackit_image.paloalto.image_id
type = "image"
}
}
resource "stackit_server" "paloalto_server" {
project_id = module.project.project_info["project_id"]
name = "paloAlto"
boot_volume = {
source_type = "volume"
source_id = stackit_volume.paloalto_vol.volume_id
}
availability_zone = var.region_az1
machine_type = var.flavor
network_interfaces = [stackit_network_interface.mgmt.network_interface_id, stackit_network_interface.ha.network_interface_id, stackit_network_interface.wan.network_interface_id]
}
resource "stackit_volume" "paloalto_vol_2" {
project_id = module.project.project_info["project_id"]
name = "PA-VM-KVM-11.2.5-root2"
availability_zone = var.region_az2
size = 100
performance_class = "storage_premium_perf2"
source = {
id = stackit_image.paloalto.image_id
type = "image"
}
}
resource "stackit_server" "paloalto_server_2" {
project_id = module.project.project_info["project_id"]
name = "paloAlto2"
boot_volume = {
source_type = "volume"
source_id = stackit_volume.paloalto_vol_2.volume_id
}
availability_zone = var.region_az2
machine_type = var.flavor
network_interfaces = [stackit_network_interface.mgmt2.network_interface_id, stackit_network_interface.ha2.network_interface_id, stackit_network_interface.wan2.network_interface_id]
}

View file

@ -0,0 +1,25 @@
resource "stackit_server_network_interface_attach" "nic-attachment-lan1" {
project_id = module.project.project_info.project_id
server_id = stackit_server.paloalto_server.server_id
network_interface_id = stackit_network_interface.lan1.network_interface_id
depends_on = [ stackit_server_network_interface_attach.nic-attachment-lan1 ]
}
resource "stackit_server_network_interface_attach" "nic-attachment-lan2" {
project_id = module.project.project_info.project_id
server_id = stackit_server.paloalto_server.server_id
network_interface_id = stackit_network_interface.lan2.network_interface_id
depends_on = [ stackit_server_network_interface_attach.nic-attachment-lan1 ]
}
resource "stackit_server_network_interface_attach" "nic-attachment-lan1_2" {
project_id = module.project.project_info.project_id
server_id = stackit_server.paloalto_server_2.server_id
network_interface_id = stackit_network_interface.lan1_2.network_interface_id
depends_on = [ stackit_server_network_interface_attach.nic-attachment-lan2 ]
}
resource "stackit_server_network_interface_attach" "nic-attachment-lan2_2" {
project_id = module.project.project_info.project_id
server_id = stackit_server.paloalto_server_2.server_id
network_interface_id = stackit_network_interface.lan2_2.network_interface_id
depends_on = [ stackit_server_network_interface_attach.nic-attachment-lan1_2 ]
}

View file

@ -0,0 +1,33 @@
resource "stackit_security_group" "paloalto" {
project_id = module.project.project_info["project_id"]
name = "test"
labels = {

Label kann entfernt werden

Label kann entfernt werden
"key" = "example"
}
}
resource "stackit_security_group_rule" "icmp_ingress" {
project_id = module.project.project_info["project_id"]
security_group_id = stackit_security_group.paloalto.security_group_id
direction = "ingress"
icmp_parameters = {
code = 0
type = 8
}
protocol = {
name = "icmp"
}
}
resource "stackit_security_group_rule" "icmp_egress" {
project_id = module.project.project_info["project_id"]
security_group_id = stackit_security_group.paloalto.security_group_id
direction = "egress"
icmp_parameters = {
code = 0
type = 8
}
protocol = {
name = "icmp"
}
}

View file

@ -0,0 +1,35 @@
# -- network variables
variable "organization_id" {
default = "03a34540-3c1a-4794-b2c6-7111ecf824ef"
}
variable "service_account_key_path" {
default = "/Users/sodan/.stackit/credentials.json"
}
variable "default_region" {
default ="eu01"
}
variable "region_az1" {
default = "eu01-1"
}
variable "region_az2" {
default = "eu01-2"
}
variable "region_az3" {
default = "eu01-3"
}
variable "region_metro" {
default = "eu01-m"
}
variable "flavor" {
type = string
description = ""
default = "m1.2"

flavor ist deprecated

flavor ist deprecated
}

View file

@ -0,0 +1,56 @@
# Palo Alto HA Setup with Terraform (STACKIT Cloud)
This Terraform configuration sets up a new project inside your organisation with an SNA as described in the .tf file.
Then two **Palo Alto Firewalls** in a **High Availability (HA)** setup on the **STACKIT Cloud IaaS** layer will be deployed.
It includes proper configuration for floating IPs (VIPs), port security, and network interface rules.
This is only example code, so please change for your needs !

"his is only example code, so please change for your needs !"
-> kein Leerzeichen zwischen Wort und Ausrufezeichen

his is only example code, so please change for your needs!

"his is only example code, so please change for your needs !" -> kein Leerzeichen zwischen Wort und Ausrufezeichen his is only example code, so please change for your needs!
---
## 🛠️ Key Concepts
### 🔁 High Availability (HA)
Two firewalls are deployed with identical network interfaces. A virtual IP (VIP) is configured for failover between the two units.
### 🧷 Port Security & VIPs
- `port_security` **must be enabled** on interfaces where the **VIP** is active.
- **Do not attach** the VIP interface to any server or instance!
- VIP must be added as an `allowed_address_pair` on **both firewalls'** relevant interfaces.
---
## ✅ Requirements
- Terraform ≥ 1.3.x
- [STACKIT Terraform Provider](https://registry.terraform.io/providers/stackitcloud/stackit/latest)
- Palo Alto VM-Series Images (pre-imported into the STACKIT project)
---
## 🚧 Limitations & Notes
- **VIP must not be attached to any instance**
The floating IP (VIP) is managed entirely by the Palo Alto HA configuration. Do **not** associate this IP statically with any compute instance via Terraform.
- **Setting CIDRs in `allowed_addresses`**
You **must** specify the VIP as a `/32` IP (e.g., `10.220.131.30/32`) — CIDR blocks (e.g., `/24`) are not supported and will be rejected or silently ignored.
You **must** specify the CIDR `0.0.0.0/0` as a second string, this is necessary for a working failover scenario.
- **Routing issues if `allowed_addresses` are missing**
If the VIP is not explicitly added to `allowed_addresses` on each port where it might be active, network traffic will fail silently due to missing neighbor/ARP entries.
- **Security groups must explicitly allow VIP traffic**
When using `port_security = true`, ensure that the correct **security group rules** allow inbound/outbound traffic for the VIP address. If omitted, traffic will be blocked.
- **Interface networks must match on both firewalls**
For a successful HA sync and failover, interfaces on both firewalls must be connected to the **same virtual networks** with matching roles (e.g., both `wan`, both `lan1`, etc.).
- **No dynamic interface switching in Terraform**
VIP failover happens on the firewall level. Terraform is **not** responsible for enabling/disabling interfaces — make sure the Palo Alto HA config is correctly set up within the OS.
- **HA Sync and Preemption is not handled by Terraform**
The logic for state sync, failover, and preemption priorities must be configured manually in the firewall GUI or CLI. This project only provisions the infrastructure.
- **floating IP switch only possible with GARP**
Important: The Floating IP will only work correctly after the move if a Gratuitous ARP (GARP) is sent out — this ensures that the IP-to-MAC binding is updated on neighboring network devices.

View file

@ -0,0 +1,18 @@
terraform {
required_providers {
stackit = {
source = "stackitcloud/stackit"
version = ">=0.50.0"
}
}
}
# Authentication
# Key flow (using path)
provider "stackit" {
default_region = var.default_region
service_account_key_path = var.service_account_key_path
enable_beta_resources = true
}

View file

@ -0,0 +1,15 @@
resource "time_sleep" "wait_before_destroy" {
destroy_duration = "60s"
}
resource "stackit_network_area" "sna" {
organization_id = var.organization_id
name = "connectivity"
network_ranges = [
{
prefix = "10.0.0.0/8"
}
]
transfer_network = "192.168.254.0/24"
depends_on = [time_sleep.wait_before_destroy]
}

View file

@ -0,0 +1,21 @@
resource "stackit_resourcemanager_project" "paloalto" {
parent_container_id = var.organization_id
name = "paloalto_ha"
owner_email = "michael.sodan@stackit.cloud"
labels = {
"networkArea" = stackit_network_area.sna.network_area_id
}
}
data "stackit_resourcemanager_project" "paloalto" {

Wozu die Datasource?

Wozu die Datasource?
project_id = stackit_resourcemanager_project.paloalto.project_id
container_id = stackit_resourcemanager_project.paloalto.container_id
}
output "project_info" {
value = {
project_id = data.stackit_resourcemanager_project.paloalto.project_id

Das geht doch auch direkt: stackit_resourcemanager_project.paloalto.project_id

Das geht doch auch direkt: stackit_resourcemanager_project.paloalto.project_id
container_id = data.stackit_resourcemanager_project.paloalto.container_id
}
}

View file

@ -0,0 +1,35 @@
# -- network variables
variable "organization_id" {
default = "03a34540-3c1a-4794-b2c6-7111ecf824ef"
}
variable "service_account_key_path" {
default = "/Users/sodan/.stackit/credentials.json"
}
variable "default_region" {
default ="eu01"
}
variable "region_az1" {
default = "eu01-1"
}
variable "region_az2" {
default = "eu01-2"
}
variable "region_az3" {
default = "eu01-3"
}
variable "region_metro" {
default = "eu01-m"
}
variable "flavor" {
type = string
description = ""
default = "c1.2"

outdated flavor

outdated flavor
}

View file

@ -3,6 +3,7 @@
General maintainers:
- Mauritz Uphoff (mauritz.uphoff@digits.schwarz)
- Michael Sodan (michael.sodan@digits.schwarz)
This example is actively maintained. The owner is responsible for reviewing and updating dependencies and functionalities on a monthly basis.
For questions, issues, or feature requests, please email general maintainers.