Terraform for Network Engineers: Part Three
Streamline Palo Alto firewall setups with a custom Terraform module for easy security policy configs!
If you have not read the previous parts of this series, I recommend you start there.
Welcome back to our journey of exploring Terraform for Network Engineers. In the previous part, we left ourself with a few challenges network engineers face when diving into the world of Terraform. Let's quickly recap those challenges:
- Setup Complexity: Are we really expecting network engineers to set up a Terraform project and write HCL code for creating resources on Panorama?
- Documentation Dive: Are network engineers supposed to dig into Terraform provider documentation to configure their desired resources?
- State File Management: What do we do with the state file? How do we manage it and share it with the team? What if it gets corrupted?
In this part, we'll tackle the first two challenges. We will explore how we can simplify the configuration file and abstract the complexity of the Terraform provider documentation.
Before we dive in, lets decompose the components of a simple Palo Alto Networks security policy configuration. A simple policy is composed of the following components:
- Device Group
- Source and Destination Zones
- Source and Destination Addresses
- Services or Destination Ports
- Application Filters
- Action - In our case, we will set it to `allow`.
Now that we have a basic understanding of the components, a possible solution to simplify the configuration file is to create an abstraction that allows network engineers to configure the security policy using a simple configuration file. This abstraction will take care of the complexity of the provider documentation and provide a simple interface to configure the security policy.
A version of the simplified configuration file may look like this:
Here we have a list of rules to configure for a zone pair. Let us make the following assumptions:
- We will be treating the
source
anddestination
as a list of IP addresses and will be used as is in the configuration. - The
services
will follow the formatprotocol-port
. (Port ranges are not supported currently) - The
application
will be a list of applications as recognised by Palo Alto Network Firewall.
The solution to achieve this abstraction is to create a custom module.
Terraform Custom Module
Quoting from HashiCorp's documentation
A module is a container for multiple resources that are used together. You can use modules to create lightweight abstractions, so that you can describe your infrastructure in terms of its architecture, rather than directly in terms of physical objects.
Modules are defined using all of the same configuration language concepts that we have already seen. This means that modules can include resources, input variables, output values, and even other child modules.
Module Setup
Let's start by creating a new directory called palo-module
and creating a few configuration files that we will use to define the module.
Providers.tf
For the module, we are going to the Palo Alto Networks provider. We will define the provider in the providers.tf
file.
Variables.tf
Next, we will define the input variables required for the module. The variables will be used to configure the security policy on the Palo Alto Networks firewall. Here we will define the variables required to configure the resource based on the components we discussed earlier.
Locals.tf
The locals.tf
file is used to define local variables and expressions that can be used throughout the module. It is also a good place to perform any transformations on the input variables.
In our case, we will loop over each rule and each service in a rule to create a list of services that needs to be configured on the Palo Alto Networks Panorama.
Main.tf
Finally, we will define the resources required to configure the security policy on the Palo Alto Networks firewall.
First, we will loop over the list of services that we computed in the locals.tf
file which we determined are required for the security policies and create a service object for each service.Second, we will loop over the list of rules and create a security policy for each rule.
In the above code, most of the configuration is self-explanatory. The only parts that may need some explanation is the services
attribute in the panos_security_rule_group
resource. So, lets break that down.
It is a conditional statement that checks if the services
is set to ["application-default"]
if yes, we set the service to ["application_default"]
if not, it next evaluates if services
is set to ["any"]
if yes, we set the services to ["any"]
if it is neither, we loop over each service in the rule and get the service object name from the panos_panorama_service_object
resource.
The way to read the conditional statement is as follows:
In addition to the condition, panos_panorama_service_object.objects["${each.value.name}-${service}"].name
also requires some explanation. This is a way to reference the service object that we created using the panos_panorama_service_object
resource earlier. The name of the service object is a combination of the rule name and the service name. This is how we can reference the service object for each service in the rule. This becomes more evident when you look at the state file.
Module Usage
Now that we have created a custom module which abstracts the complexity, lets see how we can use this module in our palo configuration file.
At this point, my directly structure looks like this:
Here we have defined a list of rules that we want to configure on Panorama between the source zone Inside
and the destination zone DMZ
. We are also souring the module from the local directory.
Let's run through the terraform workflow to see if our module works as expected.
Looking over the plan, it is creating three service objects and two security rules. Exactly what we expected. Here I am writing the plan to a file called plan
and applying it. This prevents Terraform from re-evaluating the plan during the apply stage.
At this stage if you check the Panorama, you should see the security rules configured as expected.
To further make the module a re-usable block, we can store the module in a git repository and source the module configuration from the remote source.
Wrapping up
In this part, we demonstrated how to create a custom Terraform module that simplifies the configuration of security policies on a Palo Alto Networks firewall. By abstracting the complexity of the provider documentation, our module offers an easier interface for users. We also showed how to integrate this module into a configuration file to effectively manage security policies.
For the purpose of the Demo, I have only abstracted the creation of Service Objects, there is more abstraction that can be done. Some of the possibilities are
- Creating of various other objects (network, network group, service groups...)
- Auto map port to application.
- Ability to override the ports for a application.
- Ability to override the default mapped application for a port
etc...
In the following parts, I will address the elephant in the room - state file management. We will explore how to manage the state file and share it with the team.
All the code we looked at in this article can be found in the repository below.