{"id":20074179,"url":"https://github.com/juniper/junos-terraform","last_synced_at":"2025-04-12T19:09:04.288Z","repository":{"id":37823910,"uuid":"352949614","full_name":"Juniper/junos-terraform","owner":"Juniper","description":null,"archived":false,"fork":false,"pushed_at":"2024-08-21T08:26:10.000Z","size":93053,"stargazers_count":73,"open_issues_count":11,"forks_count":24,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-04-12T19:08:55.958Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Juniper.png","metadata":{"files":{"readme":"README.md","changelog":"changelog.txt","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-03-30T09:48:18.000Z","updated_at":"2025-04-09T15:24:14.000Z","dependencies_parsed_at":"2024-01-17T19:25:32.840Z","dependency_job_id":"57d96bce-934f-4ffd-a98e-ba0aa201413e","html_url":"https://github.com/Juniper/junos-terraform","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Juniper%2Fjunos-terraform","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Juniper%2Fjunos-terraform/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Juniper%2Fjunos-terraform/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Juniper%2Fjunos-terraform/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Juniper","download_url":"https://codeload.github.com/Juniper/junos-terraform/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248618277,"owners_count":21134200,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-13T14:49:41.481Z","updated_at":"2025-04-12T19:09:04.259Z","avatar_url":"https://github.com/Juniper.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JUNOS Terraform Automation Framework (JTAF)\n\nTerraform 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!\n\nMuch 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.\n\nSo 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.\n\nTo use JTAF, you'll need machine that can run **Go, Python, Git and Terraform.** This can be Linux, OSX or Windows. Some easy to consume videos are below.\n\nIntroduction: https://youtu.be/eH24eCZc7pE\n\nInstallation: https://youtu.be/aTF7_Uscd9Q\n\nGenerate: https://youtu.be/UgsFU7UplRE\n\nExecution: https://youtu.be/Lfkc38wzhNg\n\nInterface Configuration: https://youtu.be/iCnnkDodUgQ\n\nBGP Configuration: https://youtu.be/nQVNCNCJZRc\n\n# Section Breakdown\n* [Junos-Terraform Demo (Build from scratch)](#demo)\n* [Junos-Terraform Developer Guide (Build from pre-existing junos config)](#guide)\n* [Using the Provider](#provider)\n* [Testing with Terraform](#testing)\n* [Question \u0026 Answers/ Common Problems](#questions)\n\n\u003ca id=\"demo\"\u003e\u003c/a\u003e\n# Junos-Terraform Demo (Build from scratch)\n\nFollowing this demo, developers will learn to be able to manually create custom junos-terrraform providers with the ability to configure any junos device given explicitly defined xpaths and yang files.\n\nIn this demo specifically, we will create a simple provider for a vSRX of version 19.4R1 that has the capability to do two things:\n* Add a description to an interface\n* Add an `inet` address to a sub-interface\n  \nDon't worry about commits just yet, we'll discuss that later. This demo is for the purpose of creating terrafom providers from scratch with custom device capabilities related to the use of Juniper's yang file resources.\n\nIn 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:\n\n```bash\ncd Documents/GoDev/src/github.com/Juniper\ngit clone https://github.com/Juniper/junos-terraform.git\nexport JTAF_PROJECT=/Users/dgee/Documents/GoDev/src/github.com/Juniper/junos-terraform\n\n# Let's check\necho $JTAF_PROJECT\n/Users/dgee/Documents/GoDev/src/github.com/Juniper/junos-terraform\n```\n\nHaving this variable set, helps to navigate the system without lots of tedious typing.\n\n## Install Python, Go \u0026 Terraform\n\nGo version tested with: `go1.22.0 darwin/amd64`\nPython version tested with: `3.7`\nTerraform version: `v0.12.26`\n\nOther versions beyond these will work, but this is what was tested for the writing of this document.\n\n## Copy the YANG Files \nFor 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.\n\n \u003e For developers wanting to add more capabilities to the provider, they will need to also add the neccesary `yang_files` required for those capabilites outlined by the `xpath` inputs which are listed in the `xpath_inputs.xml` file created later on \n \u003e * say you want to add firewalls or policy-option options in addition to interfaces; you will need to add the yang files along with the xpaths associated with the custom capability (See `/Samples/vsrx_tf_module_template` directory for examples of an xpath file, `xpath_example.xml`, and the corresponding `yang_files` directory)\n\nLet's put the YANG files from [Juniper's YANG GitHub repository](https://github.com/Juniper/yang.git) in to a memorable location. Let's use the home directory `/junos-terraform`. Don't worry, once the necesary files are copied over, the `/yang` folder can be removed.\n\n```bash\n/junos-terraform $ [from this directory]\n\ngit clone https://github.com/Juniper/yang.git\n# this can take some time depending on your internet connection\n\nmkdir yang_files\n\n# Note, the common-types and conf-root YANG models are dependencies\ncp yang/19.4/19.4R1/common/junos-common-types@2019-01-01.yang ./yang_files\ncp yang/19.4/19.4R1/junos-es/conf/junos-es-conf-root@2019-01-01.yang ./yang_files\n# Insert the models required here\ncp yang/19.4/19.4R1/junos-es/conf/junos-es-conf-interfaces@2019-01-01.yang ./yang_files\n```\n\nIf you wanted to remove the YANG directory, you can do it like this:\n\n```bash\ncd /junos-terraform\nrm -rf yang\n```\n\n# Run First Shell Script:  `generateFiles.sh`\n\n**Prior to this step, ensure python and go is installed.**\n\n\u003eThis file can be compiled by running `chmod +x generateFiles.sh` from the `/junos-terraform` directory followed by \n`./generateFiles.sh` to run the script. \n\n`Select \"Build a provider from scratch\" option: [1] out of the options if following along with the demo`\n\n## Below describes what the script does:\n\n\n### 1. Generates a `config.toml` File\n\n*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.*\n\nCreates a config file in the home directory. Don't worry about the xPath or fileType keys. They'll be explained shortly.\nYou can find this file `config.toml` in the home directory (/junos-terraform)\n\n```bash\nyangDir = \"$(pwd)/yang_files\"\nproviderDir = \"$(pwd)/terraform_providers\"\nxpathPath = \"$(pwd)/xpath_inputs.xml\"\nproviderName = \"vsrx\"\nfileType = \"both\"\n```\n\nYou can also replace the fileType field to `text` or `xml`. The text files are for us humans.\n\n### 2. Generates the YIN and XPath Files based on YANG files\n\nThe next step, depending on the size of YANG model/s, **may take some time**. Prepare some popcorn!\nThis step will activate a python vitual enviornment (make sure python is downloaded) and install `pyang` so it can be used\nto generate the `yin` files.\n\n```bash\ncd $JTAF_PROJECT/cmd/processYang\ngo build\n./processYang -config /path_to_junos-terraform/junos-terraform/config.toml\n# OUTPUT - WARNING \u003e\u003e This can take some time. Lack of activity does not mean broken!\n\n                    ___ _____ ___  ______\n                   |_  |_   _/ _ \\ |  ___|\n                     | | | |/ /_\\ \\| |_\n                     | | | ||  _  ||  _|\n                 /\\__/ / | || | | || |\n                 \\____/  \\_/\\_| |_/\\_|\n                           0.1.5\n-------------------------------------------------------------------------\n- Creating Yin files from Yang file directory: /path_to_junos-terraform/junos-terraform/yang_files -\n-------------------------------------------------------------------------\nYin file for junos-common-types@2019-01-01 is generated\nYin file for junos-es-conf-interfaces@2019-01-01 is generated\nYin file for junos-es-conf-root@2019-01-01 is generated\n--------------------------------------------\n- Creating _xpath files from the Yin files -\n--------------------------------------------\nCreating Xpath file: junos-common-types@2019-01-01_xpath.txt\nCreating Xpath file: junos-es-conf-interfaces@2019-01-01_xpath.txt\nCreating Xpath file: junos-es-conf-root@2019-01-01_xpath.txt\n```\n\nAt this point, `venv` is `deactivated` and the first script has terminated.\n\n# Create an XPath Input XML File\n\nGreat, at this point now we have text file and YIN versions of the YANG files. We need those for the next step.\n\nLet's create a file, which provides a list of inputs to the part of JTAF which writes the `.go` code automagically.\nThis input identifies the content of the provider that JTAF will create.\n\n\u003e For developers wanting to add more capabilities to the provider, this is where the additional xpath inputs need to be added. Assuming that the neccesary `yang_files` required for those capabilites outlined by the `xpath` inputs are added, the inputs can be incorporated into this file following the format below.\n \u003e * Again, examples of this implementation cam be found in the `/Samples/vsrx_tf_module_template` directory which include examples of an xpath file, `xpath_example.xml`, and the corresponding `yang_files` directory)\n\nCreate a file `/junos-terraform/xpath_inputs.xml` and populate it with the content below.\n\n## **Make sure that the file is named `xpath_inputs.xml`** \n* `The name must match the name defined in config.toml`\n\n```bash\n\u003cfile-list\u003e\n        \u003cxpath name=\"/interfaces/interface/description\"/\u003e\n        \u003cxpath name=\"/interfaces/interface/unit/family/inet/address/name\"/\u003e\n\u003c/file-list\u003e\n```\n\nA simple explanation of the above XPaths:\n\n* The first xpath entry is for the interface description and references a YANG leaf.\n* The second xpath entry identifies the inet YANG leaf in the YANG model.\n\nYou can view these expressions as a simple way to identify the fields inside the YANG model we're interested in.\nJTAF 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.\n\n\n# Run Second Shell Script:  `buildProvider.sh`\n\n\u003e ### **Prior to this step**\n\u003e * IF you want to test the output of the configuration from the `terraform test` files, set the ENV variable `MOCK_FILE` to the path of an empty `xml` file you create where the system can display the expected config defined in the .tf files. \n\u003e   * example: create a `jtaf_output.xml` file in the `/junos-terraform` directory and run `export MOCK_FILE=/path_to_junos-terraform/jtaf_output.xml`)\n\u003e * **Warning:** `MOCK_FILE` should be `unset` unless wanting to enter Mock mode, otherwise system will look for path setup in the terraform `.tf` test files created later on. `Mock Mode allows developers to test terraform commands and commits to a local file prior to device communication.` If `MOCK_FILE` is set, terraform commands will output to the file declared by the variable and not the device declared in the `main.tf` file.\n\n### Now let's run the script: \n\u003eThis file can be compiled by running `chmod +x buildProvider.sh` from the home directory followed by \n`./buildProvider.sh` to run the script. \n\nBelow describes what the script does:\n\n## 1. Builds the Provider Resources\n\nFirst, we need JTAF to create some `.go` code from the YANG models and XML data we provided.\n\n```bash\ncd $JTAF_PROJECT/cmd/processProviders\ngo build\n./processProviders -config /path_to_config/config.toml\n# This next step is rapid\n\n                    ___ _____ ___  ______\n                   |_  |_   _/ _ \\ |  ___|\n                     | | | |/ /_\\ \\| |_\n                     | | | ||  _  ||  _|\n                 /\\__/ / | || | | || |\n                 \\____/  \\_/\\_| |_/\\_|\n                           0.1.5\n------------------------------------------------------------\n- Autogenerating Terraform Provider code from _xpath files -\n------------------------------------------------------------\nTerraform API resource_InterfacesInterfaceDescription created\nTerraform API resource_InterfacesInterfaceUnitFamilyInetAddressName created\n# --------------------------------------------------------------------------------\nNumber of Xpaths processed:  2\nNumber of potential issues:  0\n\n---------------------------------------------\n- Copying the rest of the required Go files -\n---------------------------------------------\nCopied file: config.go to /path_to_junos-terraform/junos-terraform/terraform_providers\nCopied file: main.go to /path_to_junos-terraform/junos-terraform/terraform_providers\nCopied file: resource_junos_destroy_commit.go to /path_to_junos-terraform/junos-terraform/terraform_providers\nCopied file: resource_junos_device_commit.go to /path_to_junos-terraform/junos-terraform/terraform_providers\n-------------------\n- Creating Go Mod -\n-------------------\n```\n\nThe output of this step is written to the `/junos-terraform/terraform_provider` directory. Let's build the provider!\n\n\n## 2. Creates updated xpath input file (if needed)\n\nIf there are any found issues during the building of the provider resources, a new file called `updated_xpath_inputs.xml` will be created with a trimmed version of the provided xpath inputs file. This file can be used to replace the data in the `xpath_inputs.xml`.\n\n\n## 3. Builds the Provider\n\n```bash\ncd /terraform_providers\ngo build\n```\nThis 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.\n\n```bash\n# Validate the file kind\nfile terraform-provider-junos-device\n# terraform-provider-junos-device: Mach-O 64-bit executable x86_64\n```\n\nIf you want this provider to work with Linux, then you can cross-compile using the `GOOS` input. See below.\n\n```bash\nGOOS=linux go build -o terraform-provider-junos-device\n\nfile terraform-provider-junos-device\n\n# terraform-provider-junos-device: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=SWvAslM7UiUlMNJJOG8f/MV8jDWinx0vKkuo7Zmec/-2fk9ZDz88J7folCoc0q/ftWLT5N4tiPWQ8DlXY2J, not stripped\n```\n\nThe 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 and you are ready to use it. Jump to [Using the Provider](#provider)\n\n\n\u003ca id=\"guide\"\u003e\u003c/a\u003e\n# Junos-Terraform Developer Guide (Build from pre-existing junos config)\n\nThis section is aimed at users who are a little more comfortable with the way junos-terraform works and want to develop, test, configure Juniper devices with a pre-existing configuration.\n\n\u003e **The only requirement for this section is to upload a configuration in `xml` format to the `/junos-terraform/user_config_files` folder.**\n\u003e * When adding this `xml` file to the folder, remove any starting and ending `\u003cconfiguration\u003e` and `\u003ccli\u003e` tags.\n\u003e * For reference, see `test.xml` in the `/Samples/user_config_files` folder which contains configuration for `vqfx` spine for Junos version `23.1`\n\u003e * After configuration is uploaded, the rest is taken care of (provider build and test file templates created)\n\n### **Prior to start (for testing)**\n* IF you want to test the output of the configuration from the `terraform test` files, set the ENV variable `MOCK_FILE` to the path of an empty `xml` file you create where the system can display the expected config defined in the .tf files. \n   * example: create a `jtaf_output.xml` file in the `/junos-terraform` directory and run `export MOCK_FILE=/path_to_junos-terraform/jtaf_output.xml`)\n\n* **Warning:** `MOCK_FILE` should be `unset` unless wanting to enter Mock mode, otherwise system will look for path setup. `Mock Mode allows developers to test terraform commands and commits to a local file prior to device communication.` If `MOCK_FILE` is set, terraform commands will output to the file declared by the variable and not the device declared in the `main.tf` file.\n\n# Run Shell Script: `generateFiles.sh`\n\n**Prior to this step, ensure python and go is installed.**\n\n\u003e This file can be compiled by running `chmod +x generateFiles.sh` from the home directory followed by `./generateFiles.sh` to run the script. \n\n`Select \"Provide a configuration\" option: [2] out of the options if wanting to create a resource provider based on an existing configuration`\n\nBelow describes what the script does:\n\n### 1. Generates a `config.toml` File\n\n* Creates a config file in the home directory. Don't worry about the xPath or fileType keys. They'll be explained shortly.\n* You can find this file `config.toml` in the home directory `/junos-terraform`\n\n### 2. Copies YANG Files \n\n* This part of the script calls for the cloning of the Juniper `yang` directory in order to copy necessary yang files for the given `device` and `version`. **This will only happen if the folder `yang_files` does not exist already.**\n\n### 3. Generates the YIN and XPath Files based on YANG files\n\n* This part of the script enables the call to create YIN and Xpath files. For more details, look at the demo for this section.\n\n### 4. Creates an XPath Input XML File\n\n* This part of the script uses a go file called `createXpathInputs.go` to parse the configuration loaded by the user and creates an `xpath_inputs.xml` file. This file contains a skeleton of all the configured xpaths contained in the pre-loaded configuration. These xpaths will be used to generate the provider resources.\n\n### 5. Builds the Provider Resources\n\n* Creates updated xpath input file (if needed)\n  * If there are any found issues during the building of the provider resources, a new file called `updated_xpath_inputs.xml` will be created with a trimmed version of the provided xpath inputs file. This file can be used to replace the data in the `xpath_inputs.xml`.\n* We need JTAF to create some `.go` code from the YANG models and XML data we provided which is written to the `/terraform_provider` directory.\n\n### 6. Build the Provider\n\n* Similar to the demo, the script run the creation of the binary file `terraform-provider-junos-[device-name]` which is actually our fresh new and shiny Terraform Provider. If you got this far, congratulations. You just created a Terraform provider for Junos.\n\n### 7. Using the resouce\n\n* For the next section, refer to the `/TFtemaplates` directory created by the script providing a basic `.tf` template for the `main` and `test` files\n\n\n\u003ca id=\"provider\"\u003e\u003c/a\u003e\n# Using the new Provider\n\nTo 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!\n\n__Terraform Search Locations__\n\nMore recent versions of Terraform will look for providers (plugins) in the Hashicorp hosted registry and in a number of local locations. \n\nFirstly, 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:\n\n```bash\ntree /Users/dgee/.terraform.d\n├── checkpoint_cache\n├── checkpoint_signature\n└── plugins\n    └── juniper\n        └── providers\n            └── junos-vsrx\n                └── 19.41.101\n                    ├── darwin_amd64\n                    │   └── terraform-provider-junos-vsrx\n                    └── linux_amd64\n                        └── terraform-provider-junos-vsrx\n```\n\nNotice 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.\n\nIf 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.\n\nYou can use the method above, enabling Terraform to find the provider locally. Here's how:\n\n`mkdir -p ~/.terraform.d/plugins/juniper/providers/junos-vsrx/19.41.101/darwin_amd64`\n\nIt's probably a good idea to replace the `juniper` part with your own organisation's name to prevent any confusion.\n\n**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.\n\n```bash\nprovider_installation {\n  filesystem_mirror {\n    path = \"/path_to_junos-terraform/junos-terraform/plugins\"\n    include = [\"*/*/*\"]\n  }\n}\n```\n\nMake sure that the same file tree exists from `plugins` as before.\n\nThe other option of course, is to publish to your provider/s to the Hashicorp registry and not have them stored locally.\n\n\u003ca id=\"testing\"\u003e\u003c/a\u003e\n## __Testing With Terraform__\n\nOk, now we've got the Terraform provider in place, we can actually test Terraform! For this section, you will replace the `provider` section with the information for the device which is being configured. \n\n\u003e If testing the provider from scatch, skip this message. If building a provider from a pre-loaded configuration, the following steps have been more or less done for you. Look for the `junos-terraform/TFtemplates` which will have prcompiled test files to use for testing. The `testbed` and required files and folders have also already been made for you. The only requirment is to manually fill in the resource information.\n\nYou are free to choose a directory in which to test this. I'm going to stick with the home `/junos-terraform` directory.\n\n```bash\ncd /junos-terraform\nmkdir testbed \u0026\u0026 cd testbed\n```\n\nWe need to create a number of files.\n\n```bash\nmkdir vsrx_1\ntouch main.tf\ntouch vsrx_1/main.tf\n```\n\nContent of `main.tf`\n```bash\nterraform {\n  required_providers {\n    junos-vsrx = {\n      source = \"juniper/providers/junos-vsrx\"\n      version = \"19.41.101\"\n    }\n  }\n}\n\nprovider \"junos-vsrx\" {\n    host = \"localhost\"\n    port = 8300\n    username = \"root\"\n    password = \"juniper123\"\n    sshkey = \"\"\n}\n\nmodule \"vsrx_1\" {\n  source = \"./vsrx_1\"\n\n  providers = {junos-vsrx = junos-vsrx}\n\n  depends_on = [junos-vsrx_destroycommit.commit-main]\n}\n\nresource \"junos-vsrx_commit\" \"commit-main\" {\n  resource_name = \"commit\"\n  depends_on = [module.vsrx_1]\n}\n\nresource \"junos-vsrx_destroycommit\" \"commit-main\" {\n  resource_name = \"destroycommit\"\n}\n```\n\nContent of `vsrx_1/main.tf`\n\n```bash\nterraform {\n  required_providers {\n    junos-vsrx = {\n      source = \"juniper/providers/junos-vsrx\"\n      version = \"19.41.101\"\n    }\n  }\n}\n\nresource \"junos-vsrx_InterfacesInterfaceDescription\" \"vsrx_1\" {\n    resource_name = \"vsrx_1\"\n    name = \"ge-0/0/0\"\n    description = \"Test description\"\n}\n\nresource \"junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName\" \"vsrx_2\" {\n    resource_name = \"vsrx_2\"\n    name = \"ge-0/0/0\"\n    name__1 = \"0\"\n    name__2 = \"10.0.0.1/24\"\n}\n```\n\n__Let's Initialise Terraform__\n\nWe're getting so close! Let's initialize Terraform. From the `testbed` folder, run: \n\n```bash\ntestbed $ terraform init\n\nInitializing modules...\n\nInitializing the backend...\n\nInitializing provider plugins...\n- Finding juniper/providers/junos-vsrx versions matching \"19.41.101\"...\n- Installing juniper/providers/junos-vsrx v19.41.101...\n- Installed juniper/providers/junos-vsrx v19.41.101 (unauthenticated)\n\nTerraform has created a lock file .terraform.lock.hcl to record the provider\nselections it made above. Include this file in your version control repository\nso that Terraform can guarantee to make the same selections by default when\nyou run \"terraform init\" in the future.\n\nTerraform has been successfully initialized!\n\nYou may now begin working with Terraform. Try running \"terraform plan\" to see\nany changes that are required for your infrastructure. All Terraform commands\nshould now work.\n\nIf you ever set or change modules or backend configuration for Terraform,\nrerun this command to reinitialize your working directory. If you forget, other\ncommands will detect it and remind you to do so if necessary.\n```\n\nThe next step is to actually run the plan and apply steps.\n\n```bash\ntestbed $ terraform plan\n\nTerraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:\n  + create\n\nTerraform will perform the following actions:\n\n  # junos-vsrx_commit.commit-main will be created\n  + resource \"junos-vsrx_commit\" \"commit-main\" {\n      + id            = (known after apply)\n      + resource_name = \"commit\"\n    }\n\n  # junos-vsrx_destroycommit.commit-main will be created\n  + resource \"junos-vsrx_destroycommit\" \"commit-main\" {\n      + id            = (known after apply)\n      + resource_name = \"destroycommit\"\n    }\n\n  # module.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1 will be created\n  + resource \"junos-vsrx_InterfacesInterfaceDescription\" \"vsrx_1\" {\n      + description   = \"Test description\"\n      + id            = (known after apply)\n      + name          = \"ge-0/0/0\"\n      + resource_name = \"vsrx_1\"\n    }\n\n  # module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2 will be created\n  + resource \"junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName\" \"vsrx_2\" {\n      + id            = (known after apply)\n      + name          = \"ge-0/0/0\"\n      + name__1       = \"0\"\n      + name__2       = \"10.0.0.1/24\"\n      + resource_name = \"vsrx_2\"\n    }\n\nPlan: 4 to add, 0 to change, 0 to destroy.\n\n─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\n\nNote: 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.\n```\n\nOur 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.\n\n```bash\ntestbed $ terraform apply -auto-approve\n\nTerraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:\n  + create\n\nTerraform will perform the following actions:\n\n  # junos-vsrx_commit.commit-main will be created\n  + resource \"junos-vsrx_commit\" \"commit-main\" {\n      + id            = (known after apply)\n      + resource_name = \"commit\"\n    }\n\n  # junos-vsrx_destroycommit.commit-main will be created\n  + resource \"junos-vsrx_destroycommit\" \"commit-main\" {\n      + id            = (known after apply)\n      + resource_name = \"destroycommit\"\n    }\n\n  # module.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1 will be created\n  + resource \"junos-vsrx_InterfacesInterfaceDescription\" \"vsrx_1\" {\n      + description   = \"Test description\"\n      + id            = (known after apply)\n      + name          = \"ge-0/0/0\"\n      + resource_name = \"vsrx_1\"\n    }\n\n  # module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2 will be created\n  + resource \"junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName\" \"vsrx_2\" {\n      + id            = (known after apply)\n      + name          = \"ge-0/0/0\"\n      + name__1       = \"0\"\n      + name__2       = \"10.0.0.1/24\"\n      + resource_name = \"vsrx_2\"\n    }\n\nPlan: 4 to add, 0 to change, 0 to destroy.\njunos-vsrx_destroycommit.commit-main: Creating...\njunos-vsrx_destroycommit.commit-main: Creation complete after 0s [id=localhost_destroycommit]\nmodule.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1: Creating...\nmodule.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Creating...\nmodule.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Creation complete after 5s [id=localhost_vsrx_2]\nmodule.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1: Still creating... [10s elapsed]\nmodule.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1: Creation complete after 10s [id=localhost_vsrx_1]\njunos-vsrx_commit.commit-main: Creating...\njunos-vsrx_commit.commit-main: Creation complete after 6s [id=localhost_commit]\n\nApply complete! Resources: 4 added, 0 changed, 0 destroyed.\n```\n\nLet's just check the input on the vSRX instance to make sure we have the correct configs!\n\nTerraform 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.\n\n```bash\n# show interfaces ge-0/0/0 | display inheritance no-comments\ndescription \"Test description\";\nunit 0 {\n    family inet {\n        address 10.0.0.1/24;\n    }\n}\n```\n\nHere are the groups:\n\n```bash\n# show groups\nvsrx_2 {\n    interfaces {\n        ge-0/0/0 {\n            unit 0 {\n                family inet {\n                    address 10.0.0.1/24;\n                }\n            }\n        }\n    }\n}\nvsrx_1 {\n    interfaces {\n        ge-0/0/0 {\n            description \"Test description\";\n        }\n    }\n}\n```\n\n## Terraform \u0026 Junos Commits\n\nJunos and Terraform are not natural buddies. Their life-cycles are somewhat orthogonal in nature.\n\nWe 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. \n\nThankfully, 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!\n\nYou might have noticed in the top level `main.tf` file, there was some `depends_on` Terraform HCL keys.\n\nIt's by using these concrete dependencies, we are able to trigger the resources `commit` and `destroycommit` to be created.\n\nThe dependency order is thus:\n\n1. The `commit` resource depends on the module\n2. The module contains the actual desired state (which may have further ordered structure)\n3. The module depends on the `destroycommit` resource\n\n\nThis 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.\n\n__Making Changes__\n\nWhen 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.\n\nLet's try it out in context of this demonstration. In the `vsrx_1/main.tf`, change the inet address to `.2` instead of `.1`.\nRun the `terraform taint` command and re-run the plan and apply sequences.\n\n```bash\nterraform taint junos-vsrx_commit.commit-main\n\njunos-vsrx_destroycommit.commit-main: Refreshing state... [id=localhost_destroycommit]\nmodule.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Refreshing state... [id=localhost_vsrx_2]\nmodule.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1: Refreshing state... [id=localhost_vsrx_1]\njunos-vsrx_commit.commit-main: Refreshing state... [id=localhost_commit]\n\nTerraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:\n  ~ update in-place\n-/+ destroy and then create replacement\n\nTerraform will perform the following actions:\n\n  # junos-vsrx_commit.commit-main is tainted, so must be replaced\n-/+ resource \"junos-vsrx_commit\" \"commit-main\" {\n      ~ id            = \"localhost_commit\" -\u003e (known after apply)\n        # (1 unchanged attribute hidden)\n    }\n\n  # module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2 will be updated in-place\n  ~ resource \"junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName\" \"vsrx_2\" {\n        id            = \"localhost_vsrx_2\"\n        name          = \"ge-0/0/0\"\n      ~ name__2       = \"10.0.0.1/24\" -\u003e \"10.0.0.2/24\"\n        # (2 unchanged attributes hidden)\n    }\n\nPlan: 1 to add, 1 to change, 1 to destroy.\n\n─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\n\nNote: 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.\n\n# Now run the apply\n terraform apply -auto-approve\njunos-vsrx_destroycommit.commit-main: Refreshing state... [id=localhost_destroycommit]\nmodule.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1: Refreshing state... [id=localhost_vsrx_1]\nmodule.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Refreshing state... [id=localhost_vsrx_2]\njunos-vsrx_commit.commit-main: Refreshing state... [id=localhost_commit]\n\nTerraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:\n  ~ update in-place\n-/+ destroy and then create replacement\n\nTerraform will perform the following actions:\n\n  # junos-vsrx_commit.commit-main is tainted, so must be replaced\n-/+ resource \"junos-vsrx_commit\" \"commit-main\" {\n      ~ id            = \"localhost_commit\" -\u003e (known after apply)\n        # (1 unchanged attribute hidden)\n    }\n\n  # module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2 will be updated in-place\n  ~ resource \"junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName\" \"vsrx_2\" {\n        id            = \"localhost_vsrx_2\"\n        name          = \"ge-0/0/0\"\n      ~ name__2       = \"10.0.0.1/24\" -\u003e \"10.0.0.2/24\"\n        # (2 unchanged attributes hidden)\n    }\n\nPlan: 1 to add, 1 to change, 1 to destroy.\njunos-vsrx_commit.commit-main: Destroying... [id=localhost_commit]\njunos-vsrx_commit.commit-main: Destruction complete after 0s\nmodule.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Modifying... [id=localhost_vsrx_2]\nmodule.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Modifications complete after 5s [id=localhost_vsrx_2]\njunos-vsrx_commit.commit-main: Creating...\njunos-vsrx_commit.commit-main: Creation complete after 6s [id=localhost_commit]\n\nApply complete! Resources: 1 added, 1 changed, 1 destroyed.\n\n# Re-run the plan to confirm!\nterraform plan\njunos-vsrx_destroycommit.commit-main: Refreshing state... [id=localhost_destroycommit]\nmodule.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Refreshing state... [id=localhost_vsrx_2]\nmodule.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1: Refreshing state... [id=localhost_vsrx_1]\njunos-vsrx_commit.commit-main: Refreshing state... [id=localhost_commit]\n\nNo changes. Your infrastructure matches the configuration.\n\nTerraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.\n```\n\nYou can also check the Junos config to make sure the reflected change has happened and that the commit has been executed.\n\n```bash\nshow interfaces ge-0/0/0 | display inheritance no-comments\ndescription \"Test description\";\nunit 0 {\n    family inet {\n        address 10.0.0.2/24;\n    }\n}\n```\n\nThere we have it.\n\nNow for the fun part! Let's clean up and destroy the Terraform state.\n\n```bash\nterraform destroy -auto-approve\njunos-vsrx_destroycommit.commit-main: Refreshing state... [id=localhost_destroycommit]\nmodule.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1: Refreshing state... [id=localhost_vsrx_1]\nmodule.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Refreshing state... [id=localhost_vsrx_2]\njunos-vsrx_commit.commit-main: Refreshing state... [id=localhost_commit]\n\nTerraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:\n  - destroy\n\nTerraform will perform the following actions:\n\n  # junos-vsrx_commit.commit-main will be destroyed\n  - resource \"junos-vsrx_commit\" \"commit-main\" {\n      - id            = \"localhost_commit\" -\u003e null\n      - resource_name = \"commit\" -\u003e null\n    }\n\n  # junos-vsrx_destroycommit.commit-main will be destroyed\n  - resource \"junos-vsrx_destroycommit\" \"commit-main\" {\n      - id            = \"localhost_destroycommit\" -\u003e null\n      - resource_name = \"destroycommit\" -\u003e null\n    }\n\n  # module.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1 will be destroyed\n  - resource \"junos-vsrx_InterfacesInterfaceDescription\" \"vsrx_1\" {\n      - description   = \"Test description\" -\u003e null\n      - id            = \"localhost_vsrx_1\" -\u003e null\n      - name          = \"ge-0/0/0\" -\u003e null\n      - resource_name = \"vsrx_1\" -\u003e null\n    }\n\n  # module.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2 will be destroyed\n  - resource \"junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName\" \"vsrx_2\" {\n      - id            = \"localhost_vsrx_2\" -\u003e null\n      - name          = \"ge-0/0/0\" -\u003e null\n      - name__1       = \"0\" -\u003e null\n      - name__2       = \"10.0.0.2/24\" -\u003e null\n      - resource_name = \"vsrx_2\" -\u003e null\n    }\n\nPlan: 0 to add, 0 to change, 4 to destroy.\njunos-vsrx_commit.commit-main: Destroying... [id=localhost_commit]\njunos-vsrx_commit.commit-main: Destruction complete after 0s\nmodule.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Destroying... [id=localhost_vsrx_2]\nmodule.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1: Destroying... [id=localhost_vsrx_1]\nmodule.vsrx_1.junos-vsrx_InterfacesInterfaceDescription.vsrx_1: Destruction complete after 3s\nmodule.vsrx_1.junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName.vsrx_2: Destruction complete after 5s\njunos-vsrx_destroycommit.commit-main: Destroying... [id=localhost_destroycommit]\njunos-vsrx_destroycommit.commit-main: Destruction complete after 4s\n\nDestroy complete! Resources: 4 destroyed.\n\n# You can check the vSRX manually to make sure the config has gone\n\nshow interfaces ge-0/0/0 | display inheritance no-comments\n```\n\nThere 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.\n\nYou can read more on this commit 'bookend' pattern [here](https://dave.dev/blog/2021/11/).\n\n\n\u003ca id=\"questions\"\u003e\u003c/a\u003e\n# Q\u0026A\n\n__1. When I downloaded, terraform, the commands are not recognized by my device__\n\nEnsure the that the terraform download is found in the `/usr/local/bin` directory\n\n* Go to where terraform download is located\n \u003e `cd /Downloads`\n\n* Use the sudo `command` to move it to the correct location.\n\u003e `sudo mv terraform /usr/local/bin/`\n\n__2. Where do I find the names of the resources I have created?__\n\nCheck 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:\n\n```bash\n// Output of a provider.go example\nfunc Provider() *schema.Provider {\n\treturn \u0026schema.Provider{\n\n\t\tSchema: map[string]*schema.Schema{\n\t\t\t\"host\": \u0026schema.Schema{\n\t\t\t\tType:     schema.TypeString,\n\t\t\t\tRequired: true,\n\t\t\t},\n\n\t\t\t\"port\": \u0026schema.Schema{\n\t\t\t\tType:     schema.TypeInt,\n\t\t\t\tRequired: true,\n\t\t\t},\n\n\t\t\t\"username\": \u0026schema.Schema{\n\t\t\t\tType:     schema.TypeString,\n\t\t\t\tRequired: true,\n\t\t\t},\n\n\t\t\t\"password\": \u0026schema.Schema{\n\t\t\t\tType:     schema.TypeString,\n\t\t\t\tRequired: true,\n\t\t\t},\n\t\t\t\"sshkey\": \u0026schema.Schema{\n\t\t\t\tType:     schema.TypeString,\n\t\t\t\tRequired: true,\n\t\t\t},\n\t\t},\n\n        // These are your resources\n\t\tResourcesMap: map[string]*schema.Resource{\n\t\t\t\"junos-vsrx_InterfacesInterfaceDescription\": junosInterfacesInterfaceDescription(),\n\t\t\t\"junos-vsrx_InterfacesInterfaceUnitFamilyInetAddressName\": junosInterfacesInterfaceUnitFamilyInetAddressName(),\n\t\t\t\"junos-vsrx_commit\": junosCommit(),\n\t        \"junos-vsrx_destroycommit\": junosDestroyCommit(),\n\t\t\t},\n\t\tConfigureFunc: providerConfigure,\n\t}\n}\n```\n\n__3. Running `.generte.sh` script sometimes fails due to go runtime error or pyang error.__\n* Ensure that pyang and go are installed. If so, re-run the script and it should continue.\n* If errors persist, try examining to ensure the right version and yang file types are being used. Deleting the autogenerated folders may be required. \n\n__4. Running `terraform init` is giving me errors regarding the location of the provider.__\n\n* Some systems have issues recognizing the `.terraformrc` file so if using this method, delete this file and follow below.\n* Ensure that you place the provider in the `~/.terraform.d/plugins/juniper/providers/junos-vsrx/19.41.101/darwin_amd64/` folder. \n  * If using `darwin_arm64`, make sure to rename the folder to match the device's core. \n* Once this is double-checked, check the `main.tf` to ensure that the terraform `required_providers` matches the naming of the path and try again. \n\n\n__5. Running `terraform apply` is giving me errors related to Plugin not Responding. How do I fix this?__\n\u003e This only applies when NOT using the `$MOCK_FILE` testing env variable \n* If terraform cannot connect to a Juniper Device during the `apply` stage, the message `Error: Plugin did not respond` will occur.\n  * To fix this issue, ensure that the `provider \"junos-deviceName\"` section in the `main.tf` file is correctly filled out and matches a running Junos device which can recieves data. If the host and port does not connect to a running device, the `terraform apply` will not work.\n* If using the `$MOCK_FILE` env variable --\u003e the information in  the `provider \"junos-deviceName\"` section in the `main.tf` is not relevant to the output in the log file defined by the varible.\n\n## Close\n\nThis 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!\n\n\n# CONTRIBUTORS\nJuniper Networks is actively contributing to and maintaining this repo.\n \n*Contributors:*\n* [Rahul Kumar](https://github.com/rahkumar651991)\n* [David Gee](https://github.com/davedotdev)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuniper%2Fjunos-terraform","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjuniper%2Fjunos-terraform","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuniper%2Fjunos-terraform/lists"}