Aravind Prabhakar

Systems Engineer | Networking | Security | PreSales | Cloud | Devops | AIOps

View on GitHub Linkedin Blogs Tags
20 July 2021

Using Juniper Terraform Automation Framework (JTAF) to generate Junos terraform providers

By Aravind

How to use JTAF

As we use different IaC tools, terraform is one of the hot topics in the industry. It is by Hashicorp and is written in HCL. As part of Infrastructure as code, there are several overlaps with ansible but it offers other advantages over ansible and similar tools. In this we will see how to use JTAF to generate a Junos provider and program vSRX accordingly over netconf.

more information on JTAF can be found here

Clone the Yang repository

git clone https://github.com/Juniper/yang.git

clone the JTAF repository

git clone https://github.com/Juniper/junos-terraform.git

Install go and terraform

In my case, i have the below go and terraform versions. you can download terraform and golang from the below

Go version

root@compute21:~# go version
go version go1.16.5 linux/amd64

TF version

root@compute21:~# terraform version
Terraform v1.0.2
on linux_amd64

chose the yang file for which you need to generate a terraform provider

Choose the yang file you want to generate providers on. In my example, i am choosing the securities conf file under junos-es directory. The exact file used is here

Follow the procedure from JTAF readme to compile the files.

Create the config.toml file

create the below file as config.toml under the cmd/processYang directory.

yangDir = "/home/aprabh/junos-terraform/vsrx"
providerDir = "/home/aprabh/junos-terraform/terraform_providers"
xpathPath = "/home/aprabh/junos-terraform/vsrx/xpath_test.xml"
fileType = "both"

compile to generate the xpaths and xml files

cd cmd/processYang
go build
./processYang -config config.toml
once you generate the yin, xml and xpaths, you can create the provider. 

Example of how the generated xpath file looks like

/security
/security/apply-groups
/security/apply-groups-except
/security/apply-macro
/security/apply-macro/name
/security/apply-macro/data
/security/apply-macro/data/name
/security/apply-macro/data/value
< ------- snipped --------- >

Refer the xpaths for which resources need to be generated

multiple xpaths can be refered. There would be multiple resources but a single binary. It is advisable to drill down as much as possible because certain keys like “name”, “apply groups” are used in multiple hierarchies and it leads to confusion. The naming convention for those duplicated keywords would be with numbers . example name__1, name__2 etc.

create sameple xpath file

Here i am creating a sample xpath file under directory /home/aprabh/junos-terraform/vsrx/ as “xpath_test.xml” The xpath i choose is to create zones for vSRX and place interfaces in the respective zones with system services and protocols to be enabled.

<file-list>
        <xpath name="/security/zones/security-zone/interfaces"/>
</file-list>

The file-list tag can contain mutliple such xpaths for which we want to generate the provider.

Compile the code and process

cd cmd/processProviders
go build
./processProviders -config config.toml

Generate the provider

after the above, the files are APIs are generated in the providerDir mentioned in the config.toml file.

compile the terraform provider binary using the below. The compilation depends on some of the files present under junos-terraform/terraform_providers/ so it is advisable to compile them there.

go build -o terraform-provider-junos-qfx

Use the Generated provider

The provider.go file shows the mapping of resource names to the function calls. For example in the above xpath generated provider “junos-qfx_SecurityZonesSecurity__ZoneInterfaces” is the resource name we need to refer to to create the zones and placing the interface in the created zone. The “junos-qfx_commit” is used to pass the config to the devices which helps in passing commit operation.

        ResourcesMap: map[string]*schema.Resource{
            "junos-qfx_SecurityZonesSecurity__ZoneInterfaces": junosSecurityZonesSecurity__ZoneInterfaces(),
            "junos-qfx_commit": junosQFXCommit(),
        },

Example of configuring vSRX with a new Zone

terraform {
  required_providers {
    junos-qfx = {
      source = "10.85.47.161/ns/junos-qfx"
      version = "1.0"
    }
  }
}

provider "junos-qfx" {
    host = "10.102.144.2"
    port = 830
    username = "root"
    password = "Juniper123"
    sshkey = ""
}


resource "junos-qfx_SecurityZonesSecurity__ZoneInterfaces" "intf" {
    resource_name = "TEST"
    name = "TEST1"
    name__1 = "ge-0/0/0"
    name__6 = "all"
    #name__9 = "all"
}

resource "junos-qfx_commit" "commit" {
    resource_name = "commit"
    depends_on = [
        junos-qfx_SecurityZonesSecurity__ZoneInterfaces.intf
    ]
}

Terraform init

root@compute21:/home/aprabh/junos-terraform/terraform_providers# terraform init

Initializing the backend...

Initializing provider plugins...
- Reusing previous version of 10.85.47.161/ns/junos-qfx from the dependency lock file
- Using previously-installed 10.85.47.161/ns/junos-qfx v1.0.0

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

Terraform plan and apply

root@compute21:/home/aprabh/junos-terraform/terraform_providers# terraform plan
junos-qfx_SecurityZonesSecurity__ZoneInterfaces.intf: Refreshing state... [id=10.102.144.2_TEST]
junos-qfx_commit.commit: Refreshing state... [id=10.102.144.2_commit]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following
symbols:
  ~ update in-place

Terraform will perform the following actions:

  # junos-qfx_SecurityZonesSecurity__ZoneInterfaces.intf will be updated in-place
  ~ resource "junos-qfx_SecurityZonesSecurity__ZoneInterfaces" "intf" {
        id            = "10.102.144.2_TEST"
        name          = "TEST1"
      ~ name__1       = "ge-0/0/0.0" -> "ge-0/0/0"
      + name__6       = "all"
        # (1 unchanged attribute hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.

──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run
"terraform apply" now.
root@compute21:/home/aprabh/junos-terraform/terraform_providers# terraform apply
junos-qfx_SecurityZonesSecurity__ZoneInterfaces.intf: Refreshing state... [id=10.102.144.2_TEST]
junos-qfx_commit.commit: Refreshing state... [id=10.102.144.2_commit]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following
symbols:
  ~ update in-place

Terraform will perform the following actions:

  # junos-qfx_SecurityZonesSecurity__ZoneInterfaces.intf will be updated in-place
  ~ resource "junos-qfx_SecurityZonesSecurity__ZoneInterfaces" "intf" {
        id            = "10.102.144.2_TEST"
        name          = "TEST1"
      ~ name__1       = "ge-0/0/0.0" -> "ge-0/0/0"
      + name__6       = "all"
        # (1 unchanged attribute hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

junos-qfx_SecurityZonesSecurity__ZoneInterfaces.intf: Modifying... [id=10.102.144.2_TEST]
junos-qfx_SecurityZonesSecurity__ZoneInterfaces.intf: Modifications complete after 3s [id=10.102.144.2_TEST]

Apply complete! Resources: 0 added, 1 changed, 0 destroyed.

Validate on vSRX

Once configured, each of the TF configured ones would appear as a group and an apply group would be made.

root> show configuration groups TEST
security {
    zones {
        security-zone TEST1 {
            interfaces {
                ge-0/0/0.0;
            }
        }
    }
}

check netconf logs

root> show log netconf.log | last
<hello xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><capabilities><capability>urn:ietf:params:netconf:base:1.0</capability></capabilities></hello>]]>]]>
<?xml version="1.0" encoding="UTF-8"?>
<rpc message-id="626179a0-5b50-4df5-91de-c5bd8095e044" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><get-configuration>
  <configuration>
  <groups><name>TEST</name></groups>
  </configuration>
</get-configuration>
</rpc>]]>]]>

Jul 20 18:44:32 [NETCONF] - [66340] Outgoing: <rpc-reply xmlns:junos="http://xml.juniper.net/junos/20.4R0/junos" message-id="626179a0-5b50-4df5-91de-c5bd8095e044" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
Jul 20 18:44:32 [NETCONF] - [66340] Outgoing: <configuration xmlns="http://xml.juniper.net/xnm/1.1/xnm" junos:changed-seconds="1626806672" junos:changed-localtime="2021-07-20 18:44:32 UTC">
Jul 20 18:44:32 [NETCONF] - [66340] Outgoing:     <groups>
Jul 20 18:44:32 [NETCONF] - [66340] Outgoing:         <name>TEST</name>
Jul 20 18:44:32 [NETCONF] - [66340] Outgoing:         <security>
Jul 20 18:44:32 [NETCONF] - [66340] Outgoing:             <zones>
Jul 20 18:44:32 [NETCONF] - [66340] Outgoing:                 <security-zone>
Jul 20 18:44:32 [NETCONF] - [66340] Outgoing:                     <name>TEST1</name>
Jul 20 18:44:32 [NETCONF] - [66340] Outgoing:                     <interfaces>
Jul 20 18:44:32 [NETCONF] - [66340] Outgoing:                         <name>ge-0/0/0.0</name>
Jul 20 18:44:32 [NETCONF] - [66340] Outgoing:                     </interfaces>
Jul 20 18:44:32 [NETCONF] - [66340] Outgoing:                 </security-zone>
Jul 20 18:44:32 [NETCONF] - [66340] Outgoing:             </zones>
Jul 20 18:44:32 [NETCONF] - [66340] Outgoing:         </security>
Jul 20 18:44:32 [NETCONF] - [66340] Outgoing:     </groups>
Jul 20 18:44:32 [NETCONF] - [66340] Outgoing: </configuration>
Jul 20 18:44:32 [NETCONF] - [66340] Outgoing: </rpc-reply>
Jul 20 18:44:32 [NETCONF] - [66340] Outgoing: ]]>]]>
[ junos  juniper  ] tags: junos - juniper