Panorama Configuration

In this activity you will:

  • Initialize the Terraform provider

  • Create the terraform.tfvars file

  • Learn about the provided modules

  • Assemble configuration/main.tf

For this portion of the lab, you will be using the Palo Alto Networks PAN-OS Terraform provider.

First, change to the Terraform configuration directory.

$ cd ~/terraform-iac-lab/configuration

Why Panorama?

In this lab we will be leveraging a Panorama instance to configure the VM-Series firewall we’ll be deploying. There are other options for configuring the VM-Series firewall such as using Terraform to configure the firewall directly or, even simpler, bootstrapping the firewall with a full bootstrap.xml file. However, we can make our boostrap package simpler and reusable across multiple VM-Series instances by leveraging Panorama.

In this scenario we can provide VM-Series instance with the address of its Panorama console, the Device Group and Template Stack it will be a member of, and an authorization key to register the instance securely with Panorama. Once it registers with Panorama, the VM-Series instance will be mapped into the appropriate Device Group and Template Stack and the configurations contained therein will be pushed to the firewall.

The following is an example of an init-cfg.txt file used in the bootstrap process.

type=dhcp-client
ip-address=
default-gateway=
netmask=
ipv6-address=
ipv6-default-gateway=
hostname=Ca-FW-DC1
vm-auth-key=755036225328715
panorama-server=10.5.107.20
panorama-server-2=10.5.107.21
tplname=FINANCE_TG4
dgname=finance_dg
dns-primary=10.5.6.6
dns-secondary=10.5.6.7
op-command-modes=jumbo-frame, mgmt-interface-swap**
op-cmd-dpdk-pkt-io=***
dhcp-send-hostname=yes
dhcp-send-client-id=yes
dhcp-accept-server-hostname=yes
dhcp-accept-server-domain=yes

Provider Initialization

Your first task is to set up the communications between the provider and the provided Panorama instance. There’s several ways this can be done. The IP address, username, and password (or API key) can be set as variables in Terraform, and can be typed in manually each time the Terraform plan is run, or specified on the command line using the -var command line option to terraform plan and terraform apply. You can also reference a JSON file in the provider configuration which can contain the configuration.

Another way you can accomplish this is by using environment variables. Use the following commands to add the appropriate environment variables, substituting in the values provided by the instructor:

$ export PANOS_HOSTNAME="<YOUR PANORAMA MGMT IP GOES HERE>"
$ export PANOS_USERNAME="<YOUR STUDENT NAME>"
$ export PANOS_PASSWORD="Ignite2020!"

Note

Replace the text <YOUR PANORAMA MGMT IP GOES HERE> and <YOUR STUDENT NAME> with the values provided to you by the instructor. If you’re doing this lab on your own, you’ll need your own Panorama instance.

Now, you should see the variables exported in your shell, which you can verify using the env | grep PANOS command:

PANOS_HOSTNAME=your-panorama-address
PANOS_USERNAME=studentXX
PANOS_PASSWORD=Ignite2020!

With these values defined, we can now initialize the Terraform panos provider with the following command.

$ terraform init

The provider is now ready to communicate with our Panorama instance.

Create configuration/terraform.tfvars

Our Terraform plan in this directory will create a device group, template, and template stack on our shared Panorama. So we don’t overwrite the configuration of other students in the class, create a file called terraform.tfvars and define values for the device group, template name, and template stack name:

device_group    = "studentXX-dg"
template        = "studentXX-template"
stack           = "studentXX-stack"

Replace the strings studentXX-dg, studentXX-template, and studentXX-stack with the values provided by the instructor.

Learn about the provided modules

You have been provided with two Terraform modules in the configuration/modules directory that will build out our Panorama configuration. Here’s a snippet of the contents of main.tf in the configuration/modules/network directory:

resource "panos_panorama_template" "demo_template" {
    name = var.template
}

resource "panos_panorama_template_stack" "demo_stack" {
    name      = var.stack
    templates = [panos_panorama_template.demo_template.name]
}

resource "panos_panorama_ethernet_interface" "untrust" {
    name                      = "ethernet1/1"
    comment                   = "untrust interface"
    vsys                      = "vsys1"
    mode                      = "layer3"
    enable_dhcp               = true
    create_dhcp_default_route = true
    template                  = panos_panorama_template.demo_template.name
}

resource "panos_panorama_ethernet_interface" "web" {
    name        = "ethernet1/2"
    comment     = "web interface"
    vsys        = "vsys1"
    mode        = "layer3"
    enable_dhcp = true
    template    = panos_panorama_template.demo_template.name
}

resource "panos_panorama_ethernet_interface" "db" {
    name        = "ethernet1/3"
    comment     = "database interface"
    vsys        = "vsys1"
    mode        = "layer3"
    enable_dhcp = true
    template    = panos_panorama_template.demo_template.name
}

Terraform will use this configuration to build out the contents of the template and template stack specified by the template and stack variables.

The network module also specifies some outputs that can be fed to other modules in the configuration:

output "zone_untrust" {
    value = panos_panorama_zone.untrust.name
}

output "zone_web" {
    value = panos_panorama_zone.web.name
}

output "zone_db" {
    value = panos_panorama_zone.db.name
}

output "interface_untrust" {
    value = panos_panorama_ethernet_interface.untrust.name
}

output "interface_web" {
    value = panos_panorama_ethernet_interface.web.name
}

output "interface_db" {
    value = panos_panorama_ethernet_interface.db.name
}

The module to populate the device group works in a similar fashion.

Assemble configuration/main.tf

Add the following to configuration/main.tf to build out the template and template stack on our Panorama instance:

module "networking" {
    source = "./modules/networking"

    template = var.template
    stack    = var.stack
}

Now run terraform init (you need to run init each time you add a new module) and terraform plan. You will see the Terraform provider determine what changes need to be made, and output all the changes that will be made to the configuration. If you run terraform apply, those changes will be added to the candidate configuration, but not committed (why?).

Add the next section to configuration/main.tf to build out the device group:

module "policies" {
    source = "./modules/policies"

    device_group = var.device_group

    zone_untrust = module.networking.zone_untrust
    zone_web     = module.networking.zone_web
    zone_db      = module.networking.zone_db

    interface_untrust = module.networking.interface_untrust
    interface_web     = module.networking.interface_web
    interface_db      = module.networking.interface_db
}

This module has variables for the names of zones and interfaces to avoid hard coding values. Our networking module outputs those names from what it creates, so we can chain these two modules together.

You can run terraform init, terraform plan, and terraform apply to populate the device group on Panorama.

Since Terraform is unable to commit configuration to PAN-OS on it’s own, we have provided a Golang helper program to commit your user’s changes to Panorama. You use a null resource provisioner in your main.tf to have Terraform run the program for you.

Add the following section to configuration/main.tf to issue the commit:

resource "null_resource" "commit_panorama" {
    provisioner "local-exec" {
        command = "./commit"
    }
    depends_on = [
        module.policies.security_rule_group,
        module.policies.nat_rule_group
    ]
}

Your completed configuration/main.tf should look like this:

provider "panos" {}

module "networking" {
    source = "./modules/networking"

    template = var.template
    stack    = var.stack
}

module "policies" {
    source = "./modules/policies"

    device_group = var.device_group

    zone_untrust = module.networking.zone_untrust
    zone_web     = module.networking.zone_web
    zone_db      = module.networking.zone_db

    interface_untrust = module.networking.interface_untrust
    interface_web     = module.networking.interface_web
    interface_db      = module.networking.interface_db
}

resource "null_resource" "commit_panorama" {
    provisioner "local-exec" {
        command = "./commit"
    }
    depends_on = [
        module.policies.security_rule_group,
        module.policies.nat_rule_group
    ]
}

Now, run terraform init and terraform apply to finalize the changes. Log in to the Panorama web UI and verify that your changes have been committed. You’re now ready to deploy the environment and have your firewall bootstrap from this configuration.