All Projects → Juniper → junos-terraform

Juniper / junos-terraform

Licence: Apache-2.0 license
No description, website, or topics provided.

Programming Languages

go
31211 projects - #10 most used programming language
python
139335 projects - #7 most used programming language
Dockerfile
14818 projects

JUNOS Terraform Automation Framework (JTAF)

Terraform is traditionally used for managing virtual infrastructure, but there are organisations out there that use Terraform end-to-end and also want to manage configuration state using the same methods for managing infrastructure. Sure, we can run a provisioner with Terraform, but that wasn't asked for!

Much the same as you can use Terraform to create an AWS EC2 instance, you can manage the configurational state of Junos. In essence, we treat Junos configuration as declarative resources.

So what is JTAF? It's a framework, meaning, it's an opinionated set of tools and steps that allow you to go from YANG models to a custom Junos Terraform provider. With all frameworks, there are some dependencies.

To use JTAF, you'll need machine that can run Go, Python and Terraform. This can be Linux, OSX or Windows. Some easy to consume videos are below.

Introduction: https://youtu.be/eH24eCZc7pE

Installation: https://youtu.be/aTF7_Uscd9Q

Generate: https://youtu.be/UgsFU7UplRE

Execution: https://youtu.be/Lfkc38wzhNg

Interface Configuration: https://youtu.be/iCnnkDodUgQ

BGP Configuration: https://youtu.be/nQVNCNCJZRc

Begin

We will create a simple provider for a vSRX of version 19.4R1 that has the capability to do two things:

  • Add a description to an interface
  • Add an inet address to a sub-interface

Don't worry about commits just yet, we'll discuss that later.

In this document, if you see $JTAF_PROJECT you can replace it with the path of the JTAF project on your system or create an environment variable. On the author's system, this happens to be below:

cd Documents/GoDev/src/github.com/Juniper
git clone https://github.com/Juniper/junos-terraform.git
export JTAF_PROJECT=/Users/dgee/Documents/GoDev/src/github.com/Juniper/junos-terraform

# Let's check
echo $JTAF_PROJECT
/Users/dgee/Documents/GoDev/src/github.com/Juniper/junos-terraform

Having this variable set, helps to navigate the system without lots of tedious typing.

Install Python, Go & Terraform

Go version tested with: go1.14.2 darwin/amd64 Python version tested with: 3.7 Terraform version: v0.12.26

Other versions beyond these will work, but this is what was tested for the writing of this document.

Create and Activate a Python venv

A virtual environment does nothing more than provide a separate Python environment, that's safe to install whatever you need, without affecting the global Python installation on a given system. We need to install pyang, which is a dependency of JTAF.

For this step, ensure that you're in the processYang directory of JTAF.

cd $JTAF_PROJECT/cmd/processYang
python -m venv venv
source venv/bin/activate

The prompt will change at this point indicating you are running with a Python virtual environment.

Install Pyang and Check the Version

pip install pyang
pyang -v
# pyang 2.5.2

On the system under test, the version installed by pip in to the venv was: 2.5.2.

Copy the YANG Files

For our example, our provider will only be able to create an interface description and place an inet address on a sub-interface. We only need a handful of YANG models for this.

Let's put the YANG files from Juniper's YANG GitHub repository in to a memorable location. Let's use /var/tmp/yang.

cd /var/tmp/jtaf
git clone https://github.com/Juniper/yang.git
# this can take some time depending on your internet connection

mkdir yang_files
mkdir terraform_providers

# Note, the common-types and conf-root YANG models are dependencies
cp yang/19.4/19.4R1/common/[email protected] ./yang_files
cp yang/19.4/19.4R1/junos-es/conf/[email protected] ./yang_files
# Insert the models required here
cp yang/19.4/19.4R1/junos-es/conf/[email protected] ./yang_files

If you wanted to remove the YANG directory, you can do it like this:

cd /var/tmp
rm -rf yang

Create a config.toml File

If you've never seen a TOML file before, don't worry! It's just a structured file containing configuration that can be parsed by a program, in this case the two main compiled programs that form JTAF. TOML stands for Tom's Obvious Minimal Langage.

Create a config file somewhere memorable. I'll use /var/tmp/jtaf/config.toml because why not.

Using your favourite text editor, create a file here: /var/tmp/jtaf/config.toml and put the content below into the file. Don't worry about the xPath or fileType keys. They'll be explained shortly.

yangDir = "/var/tmp/jtaf/yang_files"
providerDir = "/var/tmp/jtaf/terraform_providers"
xpathPath = "/var/tmp/jtaf/xpath_example.xml"
providerName = "vsrx"
fileType = "both"

You can also replace the fileType field to text or xml. The text files are for us humans.

Generate the YIN and XPath Files

The next step, depending on the size of YANG model/s, may take some time. Prepare some popcorn!

cd $JTAF_PROJECT/cmd/processYang
go build
./processYang -config /var/tmp/jtaf/jtaf_config.toml
# OUTPUT - WARNING >> This can take some time. Lack of activity does not mean broken!

                    ___ _____ ___  ______
                   |_  |_   _/ _ \ |  ___|
                     | | | |/ /_\ \| |_
                     | | | ||  _  ||  _|
                 /\__/ / | || | | || |
                 \____/  \_/\_| |_/\_|
                           0.1.5
-------------------------------------------------------------------------
- Creating Yin files from Yang file directory: /var/tmp/jtaf/yang_files -
-------------------------------------------------------------------------
Yin file for junos-common-types@2019-01-01 is generated
Yin file for junos-es-conf-interfaces@2019-01-01 is generated
Yin file for junos-es-conf-root@2019-01-01 is generated
--------------------------------------------
- Creating _xpath files from the Yin files -
--------------------------------------------
Creating Xpath file: junos-common-types@2019-01-01_xpath.txt
Creating Xpath file: junos-es-conf-interfaces@2019-01-01_xpath.txt
Creating Xpath file: junos-es-conf-root@2019-01-01_xpath.txt

At this point, you can also deactivate the venv with deactivate.

Create an XML XPath File

Great, now we have text file and YIN versions of the YANG files. We need those for the next step.

Let's create a file, which provides a list of inputs to the part of JTAF which writes the .go code automagically. This input identifies the content of the provider that JTAF will create. Some xpath_test.xml files are scattered are in the Samples directory.

Create a file /var/tmp/jtaf/xpath_example.xml and populate it with the content below.

<file-list>
        <xpath name="/interfaces/interface/description"/>
        <xpath name="/interfaces/interface/unit/family/inet/address/name"/>
</file-list>

A simple explanation of the above XPaths:

  • The first xpath entry is for the interface description and references a YANG leaf.
  • The second xpath entry identifies the inet YANG leaf in the YANG model.

You can view these expressions as a simple way to identify the fields inside the YANG model we're interested in. JTAF generated providers has a requirement of the smallest data set possible for each resources. That means, in a single resource you would place a description, and in another, you will place the inet address. Terraform is essentially a dependency aware declarative resource manager and so, we have to model resources in a way that's compatible with Terraform and Junos.

Build the Provider

First, we need JTAF to create some .go code from the YANG models and XML data we provided.

cd $JTAF_PROJECT/cmd/processProviders
go build
./processProviders -config /var/tmp/jtaf/config.toml
# This next step is rapid

                    ___ _____ ___  ______
                   |_  |_   _/ _ \ |  ___|
                     | | | |/ /_\ \| |_
                     | | | ||  _  ||  _|
                 /\__/ / | || | | || |
                 \____/  \_/\_| |_/\_|
                           0.1.5
------------------------------------------------------------
- Autogenerating Terraform Provider code from _xpath files -
------------------------------------------------------------
Terraform API resource_InterfacesInterfaceDescription created
Terraform API resource_InterfacesInterfaceUnitFamilyInetAddressName created
--------------------------------------------------------------------------------
Number of Xpaths processed:  2
Number of potential issues:  0

---------------------------------------------
- Copying the rest of the required Go files -
---------------------------------------------
Copied file: config.go to /var/tmp/jtaf/terraform_providers
Copied file: main.go to /var/tmp/jtaf/terraform_providers
Copied file: resource_junos_destroy_commit.go to /var/tmp/jtaf/terraform_providers
Copied file: resource_junos_device_commit.go to /var/tmp/jtaf/terraform_providers
-------------------
- Creating Go Mod -
-------------------

The output of this step is written to the /var/tmp/jtaf/terraform_provider directory. Let's build the provider!

cd /var/tmp/jtaf/terraform_providers
go build

This provider without any Go cross-compilation directives, will work on the system it's been generated with. If you happen to be on an OSX machine, then the provider will work for Terraform on OSX and the same is true for Linux, if you use JTAF on Linux, then natively the generated provider will operate on Linux. However, you can cross-compile the provider so that it will operate on another operating system and even CPU architecture.

# Validate the file kind
file terraform-provider-junos-device
# terraform-provider-junos-device: Mach-O 64-bit executable x86_64

If you want this provider to work with Linux, then you can cross-compile using the GOOS input. See below.

GOOS=linux go build -o terraform-provider-junos-device

file terraform-provider-junos-device

# terraform-provider-junos-device: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=SWvAslM7UiUlMNJJOG8f/MV8jDWinx0vKkuo7Zmec/-2fk9ZDz88J7folCoc0q/ftWLT5N4tiPWQ8DlXY2J, not stripped

The binary file terraform-provider-junos-vsrx is actually our fresh new and shiny Terraform Provider. If you got this far, congratulations. You just created a Terraform provider for Junos.

Using the new Provider

To test the provider we need to do two more things, one, put the provider where Terraform can find it and two, create a simple set of .tf files as inputs to Terraform!

Terraform Search Locations

More recent versions of Terraform will look for providers (plugins) in the Hashicorp hosted registry and in a number of local locations.

Firstly, there is a default location, which is located under ~/.terraform.d/. You need to create a hierarchy for each provider, it's version and the CPU architecture type the provider was compiled for. Here is what the author's looks like:

tree /Users/dgee/.terraform.d
├── checkpoint_cache
├── checkpoint_signature
└── plugins
    └── juniper
        └── providers
            └── junos-vsrx
                └── 19.41.101
                    ├── darwin_amd64
                    │   └── terraform-provider-junos-vsrx
                    └── linux_amd64
                        └── terraform-provider-junos-vsrx

Notice that the R is missing in the version 19.41.101. This is an official Juniper method of naming providers. It basically says: 19.4R1.01 of Junos, with version 01 of the provider. Due to semantic versioning, you'll notice that the leading zero has been stripped, leaving us with the last two digits for the provider number and any other digits in front being the Junos version patch number.

If you're building providers locally, it's worth considering how to version control them. Each provider generated can have a different set of capabilities, even for the same software release, so it's important that you keep a track through a simple version control system. MD5 hashing of the binary is also recommended, so as a worst case, you can identify the binaries by their computed hash.

You can use the method above, enabling Terraform to find the provider locally. Here's how:

mkdir -p ~/.terraform.d/plugins/juniper/junos-vsrx/19.4R1.101/darwin_amd64

It's probably a good idea to replace the juniper part with your own organisation's name to prevent any confusion.

The second way is to create a .terraformrc file in your home directory, where you tell Terraform where to look for the same file structure. That can be in a project directory if you so wish. Here is an example of that file.

provider_installation {
  filesystem_mirror {
    path = "/var/tmp/jtaf/plugins"
    include = ["*/*/*"]
  }
}

Make sure that the same file tree exists from plugins as before.

The other option of course, is to publish to your provider/s to the Hashicorp registry and not have them stored locally.

Testing With Terraform

Ok, now we've got the Terraform provider in place, we can actually test Terraform!

You are free to choose a directory in which to test this. I'm going to stick with the /var/tmp/jtaf directory.

cd /var/tmp/jtaf
mkdir testbed && cd testbed

We need to create a number of files.

mkdir vsrx_1
touch main.tf
touch vsrx_1/main.tf

Content of main.tf

terraform {
  required_providers {
    junos-vsrx = {
      source = "juniper/providers/junos-vsrx"
      version = "19.41.101"
    }
  }
}

provider "junos-vsrx" {
    host = "localhost"
    port = 8300
    username = "root"
    password = "juniper123"
    sshkey = ""
}

module "vsrx_1" {
  source = "./vsrx_1"

  providers = {junos-vsrx = junos-vsrx}

  depends_on = [junos-vsrx_destroycommit.commit-main]
}

resource "junos-vsrx_commit" "commit-main" {
  resource_name = "commit"
  depends_on = [module.vsrx_1]
}

resource "junos-vsrx_destroycommit" "commit-main" {
  resource_name = "destroycommit"
}

Content of vsrx_1/main.tf

terraform {
  required_providers {
    junos-vsrx = {
      source = "juniper/providers/junos-vsrx"
      version = "19.41.101"
    }
  }
}

resource "junos-vsrx_InterfacesInterfaceDescription" "vsrx_1" {
    resource_name = "vsrx_1"
    name = "ge-0/0/0"
    description = "Test description"
}

resource "junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName" "vsrx_2" {
    resource_name = "vsrx_2"
    name = "ge-0/0/0"
    name__1 = "0"
    name__2 = "10.0.0.1/24"
}

Let's Initialise Terraform

We're getting so close! Let's initialise Terraform.

Initializing modules...

Initializing the backend...

Initializing provider plugins...
- Finding juniper/providers/junos-vsrx versions matching "19.41.101"...
- Installing juniper/providers/junos-vsrx v19.41.101...
- Installed juniper/providers/junos-vsrx v19.41.101 (unauthenticated)

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

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.

The next step is to actually run the plan and apply steps.

terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # junos-vsrx_commit.commit-main will be created
  + resource "junos-vsrx_commit" "commit-main" {
      + id            = (known after apply)
      + resource_name = "commit"
    }

  # junos-vsrx_destroycommit.commit-main will be created
  + resource "junos-vsrx_destroycommit" "commit-main" {
      + id            = (known after apply)
      + resource_name = "destroycommit"
    }

  # module.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1 will be created
  + resource "junos-vsrx_InterfacesInterfaceDescription" "vsrx_1" {
      + description   = "Test description"
      + id            = (known after apply)
      + name          = "ge-0/0/0"
      + resource_name = "vsrx_1"
    }

  # module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2 will be created
  + resource "junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName" "vsrx_2" {
      + id            = (known after apply)
      + name          = "ge-0/0/0"
      + name__1       = "0"
      + name__2       = "10.0.0.1/24"
      + resource_name = "vsrx_2"
    }

Plan: 4 to add, 0 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.

Our plan is simple! Because we do not have any local Terraform state, the plan has been generated quickly and it's straight forward to read. The commit and destroycommit resources will be covered after this step. We can also tell Terraform to auto-approve the apply without any further manual input.

terraform apply -auto-approve

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # junos-vsrx_commit.commit-main will be created
  + resource "junos-vsrx_commit" "commit-main" {
      + id            = (known after apply)
      + resource_name = "commit"
    }

  # junos-vsrx_destroycommit.commit-main will be created
  + resource "junos-vsrx_destroycommit" "commit-main" {
      + id            = (known after apply)
      + resource_name = "destroycommit"
    }

  # module.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1 will be created
  + resource "junos-vsrx_InterfacesInterfaceDescription" "vsrx_1" {
      + description   = "Test description"
      + id            = (known after apply)
      + name          = "ge-0/0/0"
      + resource_name = "vsrx_1"
    }

  # module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2 will be created
  + resource "junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName" "vsrx_2" {
      + id            = (known after apply)
      + name          = "ge-0/0/0"
      + name__1       = "0"
      + name__2       = "10.0.0.1/24"
      + resource_name = "vsrx_2"
    }

Plan: 4 to add, 0 to change, 0 to destroy.
junos-vsrx_destroycommit.commit-main: Creating...
junos-vsrx_destroycommit.commit-main: Creation complete after 0s [id=localhost_destroycommit]
module.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1: Creating...
module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Creating...
module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Creation complete after 5s [id=localhost_vsrx_2]
module.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1: Still creating... [10s elapsed]
module.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1: Creation complete after 10s [id=localhost_vsrx_1]
junos-vsrx_commit.commit-main: Creating...
junos-vsrx_commit.commit-main: Creation complete after 6s [id=localhost_commit]

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

Let's just check the input on the vSRX instance to make sure we have the correct configs!

Terraform deals with configuration as a set of remote resources, which are stored in Junos configuration groups. These groups are then inherited by Junos at commit time. Here is the inherited configuration.

# show interfaces ge-0/0/0 | display inheritance no-comments
description "Test description";
unit 0 {
    family inet {
        address 10.0.0.1/24;
    }
}

Here are the groups:

# show groups
vsrx_2 {
    interfaces {
        ge-0/0/0 {
            unit 0 {
                family inet {
                    address 10.0.0.1/24;
                }
            }
        }
    }
}
vsrx_1 {
    interfaces {
        ge-0/0/0 {
            description "Test description";
        }
    }
}

Terraform & Junos Commits

Junos and Terraform are not natural buddies. Their life-cycles are somewhat orthogonal in nature.

We handle the commit based transactional nature of Junos by using a simple commit pattern, in which Terraform creates phony commit resources. The resources are only tracked locally as commits are nothing more than Junos remote procedure calls.

Thankfully, Terraform has a way of creating logical groupings, called modules. Terraform modules represent re-usable chunks of logic, in which we can give a name. Because we can name these groups, it means we can also place dependencies up on them!

You might have noticed in the top level main.tf file, there was some depends_on Terraform HCL keys.

It's by using these concrete dependencies, we are able to trigger the resources commit and destroycommit to be created.

The dependency order is thus:

  1. The commit resource depends on the module
  2. The module contains the actual desired state (which may have further ordered structure)
  3. The module depends on the destroycommit resource

This ordering means the destroycommit is created first. No action is taken when this resource is created other than local state is stored on the system. The contents of the module are executed next, which consists of NETCONF sessions being made against the target system and stored in configuration groups, which are applied. Lastly, the commit resource is created, which actually runs a commit on Junos via a NETCONF RPC.

Making Changes

When you make either a change on Junos, or a change to the local resorce .tf files, then run a Terraform plan, Terraform doesn't understand a commit must be run. Therefore you must a terraform taint $terraform_address.commit command against the commit resource, which tells Terraform to re-run the commit after making other changes in the module.

Let's try it out in context of this demonstration. In the vsrx_1/main.tf, change the inet address to .2 instead of .1. Run the terraform taint command and re-run the plan and apply sequences.

terraform taint junos-vsrx_commit.commit-main

junos-vsrx_destroycommit.commit-main: Refreshing state... [id=localhost_destroycommit]
module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Refreshing state... [id=localhost_vsrx_2]
module.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1: Refreshing state... [id=localhost_vsrx_1]
junos-vsrx_commit.commit-main: Refreshing state... [id=localhost_commit]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # junos-vsrx_commit.commit-main is tainted, so must be replaced
-/+ resource "junos-vsrx_commit" "commit-main" {
      ~ id            = "localhost_commit" -> (known after apply)
        # (1 unchanged attribute hidden)
    }

  # module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2 will be updated in-place
  ~ resource "junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName" "vsrx_2" {
        id            = "localhost_vsrx_2"
        name          = "ge-0/0/0"
      ~ name__2       = "10.0.0.1/24" -> "10.0.0.2/24"
        # (2 unchanged attributes hidden)
    }

Plan: 1 to add, 1 to change, 1 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.

# Now run the apply
 terraform apply -auto-approve
junos-vsrx_destroycommit.commit-main: Refreshing state... [id=localhost_destroycommit]
module.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1: Refreshing state... [id=localhost_vsrx_1]
module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Refreshing state... [id=localhost_vsrx_2]
junos-vsrx_commit.commit-main: Refreshing state... [id=localhost_commit]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  ~ update in-place
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # junos-vsrx_commit.commit-main is tainted, so must be replaced
-/+ resource "junos-vsrx_commit" "commit-main" {
      ~ id            = "localhost_commit" -> (known after apply)
        # (1 unchanged attribute hidden)
    }

  # module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2 will be updated in-place
  ~ resource "junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName" "vsrx_2" {
        id            = "localhost_vsrx_2"
        name          = "ge-0/0/0"
      ~ name__2       = "10.0.0.1/24" -> "10.0.0.2/24"
        # (2 unchanged attributes hidden)
    }

Plan: 1 to add, 1 to change, 1 to destroy.
junos-vsrx_commit.commit-main: Destroying... [id=localhost_commit]
junos-vsrx_commit.commit-main: Destruction complete after 0s
module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Modifying... [id=localhost_vsrx_2]
module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Modifications complete after 5s [id=localhost_vsrx_2]
junos-vsrx_commit.commit-main: Creating...
junos-vsrx_commit.commit-main: Creation complete after 6s [id=localhost_commit]

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

# Re-run the plan to confirm!
terraform plan
junos-vsrx_destroycommit.commit-main: Refreshing state... [id=localhost_destroycommit]
module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Refreshing state... [id=localhost_vsrx_2]
module.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1: Refreshing state... [id=localhost_vsrx_1]
junos-vsrx_commit.commit-main: Refreshing state... [id=localhost_commit]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.

You can also check the Junos config to make sure the reflected change has happened and that the commit has been executed.

show interfaces ge-0/0/0 | display inheritance no-comments
description "Test description";
unit 0 {
    family inet {
        address 10.0.0.2/24;
    }
}

There we have it.

Now for the fun part! Let's clean up and destroy the Terraform state.

terraform destroy -auto-approve
junos-vsrx_destroycommit.commit-main: Refreshing state... [id=localhost_destroycommit]
module.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1: Refreshing state... [id=localhost_vsrx_1]
module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Refreshing state... [id=localhost_vsrx_2]
junos-vsrx_commit.commit-main: Refreshing state... [id=localhost_commit]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # junos-vsrx_commit.commit-main will be destroyed
  - resource "junos-vsrx_commit" "commit-main" {
      - id            = "localhost_commit" -> null
      - resource_name = "commit" -> null
    }

  # junos-vsrx_destroycommit.commit-main will be destroyed
  - resource "junos-vsrx_destroycommit" "commit-main" {
      - id            = "localhost_destroycommit" -> null
      - resource_name = "destroycommit" -> null
    }

  # module.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1 will be destroyed
  - resource "junos-vsrx_InterfacesInterfaceDescription" "vsrx_1" {
      - description   = "Test description" -> null
      - id            = "localhost_vsrx_1" -> null
      - name          = "ge-0/0/0" -> null
      - resource_name = "vsrx_1" -> null
    }

  # module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2 will be destroyed
  - resource "junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName" "vsrx_2" {
      - id            = "localhost_vsrx_2" -> null
      - name          = "ge-0/0/0" -> null
      - name__1       = "0" -> null
      - name__2       = "10.0.0.2/24" -> null
      - resource_name = "vsrx_2" -> null
    }

Plan: 0 to add, 0 to change, 4 to destroy.
junos-vsrx_commit.commit-main: Destroying... [id=localhost_commit]
junos-vsrx_commit.commit-main: Destruction complete after 0s
module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Destroying... [id=localhost_vsrx_2]
module.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1: Destroying... [id=localhost_vsrx_1]
module.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1: Destruction complete after 3s
module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Destruction complete after 5s
junos-vsrx_destroycommit.commit-main: Destroying... [id=localhost_destroycommit]
junos-vsrx_destroycommit.commit-main: Destruction complete after 4s

Destroy complete! Resources: 4 destroyed.

# You can check the vSRX manually to make sure the config has gone

show interfaces ge-0/0/0 | display inheritance no-comments

There is some inverse logic here. The destroycommit is 'created' on the delete cycle within the provider, whereas the commit is 'created' on the create cycle. Terraform providers do nothing more than CRUD (create/read/update/delete) on data structures, which in turn have methods on them. The Terraform apply cycle runs create and update. Terraform destroy in turns runs the delete method.

You can read more on this commit 'bookend' pattern here.

Q&A

1. Where do I find the names of the resources I have created?

Check in the provider.go file that is dynamically generated. You will find a data structure with this signature: ResourcesMap: map[string]*schema.Resource. Your resources are named in a map. Here is an example:

// Output of a provider.go example
func Provider() *schema.Provider {
	return &schema.Provider{

		Schema: map[string]*schema.Schema{
			"host": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			"port": &schema.Schema{
				Type:     schema.TypeInt,
				Required: true,
			},

			"username": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},

			"password": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
			"sshkey": &schema.Schema{
				Type:     schema.TypeString,
				Required: true,
			},
		},

        // These are your resources
		ResourcesMap: map[string]*schema.Resource{
			"junos-vsrx_InterfacesInterfaceDescription": junosInterfacesInterfaceDescription(),
			"junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName": junosInterfacesInterfaceUnitFamilyInetAddressName(),
			"junos-vsrx_commit": junosCommit(),
	        "junos-vsrx_destroycommit": junosDestroyCommit(),
			},
		ConfigureFunc: providerConfigure,
	}
}

Close

This covers the use of JTAF with a full example. Please post any issues or bug reports using GitHub issues at the top of this page. Enjoy!

CONTRIBUTORS

Juniper Networks is actively contributing to and maintaining this repo.

Contributors:

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].