diff options
| author | Yuriy Andamasov <yuriy@vyos.io> | 2026-04-29 06:35:31 +0300 |
|---|---|---|
| committer | Yuriy Andamasov <yuriy@vyos.io> | 2026-05-06 16:18:03 +0300 |
| commit | 9277e2f189115d9c544834f77fb216eaf3711407 (patch) | |
| tree | e7fda1b7ea00bef67fd8a23cf541cf4067236b93 /docs/automation | |
| parent | e87bfdfc7483af48b54bb8a6993a750c568c2310 (diff) | |
| download | vyos-documentation-9277e2f189115d9c544834f77fb216eaf3711407.tar.gz vyos-documentation-9277e2f189115d9c544834f77fb216eaf3711407.zip | |
feat: activate 106 visual-validated canaries via swap
Imports 105 MD files (plus quick-start already present) from
origin/myst/current and adds them to docs/_swap.txt. The selection
is the BackstopJS visual-passers cohort: pages with <5% rendered
diff vs the live RST docs at docs.vyos.io/en/latest/, filtered to
those with an RST counterpart on current and no cmdincludemd usage
(template-format reconciliation pending).
Local sphinx-build with all 106 swapped: succeeded with 100
warnings (vs 95 baseline). The 5 new warnings are all undefined
cross-reference labels, not build failures:
- contributing/development.md (missing 'coding-guidelines')
- operation/upgrade-recovery.md (3 missing 'how_it_works' /
'cancelling_recovery')
- vpp/configuration/dataplane/{buffers,memory,unix}.md (missing
'vpp_config_dataplane_*' labels)
Source list: ~/.claude/projects/-Users-vybot-GitHub-vyos-documentation/docs/2026-04-29-myst-conversion-audit/visual-passers-under-5pct.txt
BackstopJS report: claude/gifted-hertz-74b9f9 worktree
(visual-compare/), 2026-04-23 vs vyos--1838.org.readthedocs.build.
🤖 Generated by [robots](https://vyos.io)
Diffstat (limited to 'docs/automation')
| -rw-r--r-- | docs/automation/md-command-scripting.md | 216 | ||||
| -rw-r--r-- | docs/automation/md-index.md | 16 | ||||
| -rw-r--r-- | docs/automation/md-vyos-ansible.md | 99 | ||||
| -rw-r--r-- | docs/automation/md-vyos-govyos.md | 186 | ||||
| -rw-r--r-- | docs/automation/md-vyos-napalm.md | 152 | ||||
| -rw-r--r-- | docs/automation/md-vyos-netmiko.md | 76 | ||||
| -rw-r--r-- | docs/automation/md-vyos-pyvyos.md | 138 | ||||
| -rw-r--r-- | docs/automation/terraform/md-index.md | 28 |
8 files changed, 911 insertions, 0 deletions
diff --git a/docs/automation/md-command-scripting.md b/docs/automation/md-command-scripting.md new file mode 100644 index 00000000..c1e1c239 --- /dev/null +++ b/docs/automation/md-command-scripting.md @@ -0,0 +1,216 @@ +--- +lastproofread: '2026-03-16' +--- + +(command-scripting)= + +# Command scripting + +VyOS supports executing configuration and operational commands non-interactively +from shell scripts. + +To include VyOS-specific functions and aliases, source the +`/opt/vyatta/etc/functions/script-template` file at the beginning of your +script. + +```none +#!/bin/vbash +source /opt/vyatta/etc/functions/script-template +exit +``` + +## Script execute permissions + +Simply placing script files in `/config/scripts/` does not mean the system +can execute them. + +To make your scripts executable, grant them **execute permissions**. Use the +following command: + +```none +chmod +x /config/scripts/script-name.sh +``` + +## Run configuration commands + +In scripts, present configuration commands as in a standard configuration +session. + +For example, to disable a BGP peer during a VRRP transition to the backup +state, use the following syntax: + +```none +#!/bin/vbash +source /opt/vyatta/etc/functions/script-template +configure +set protocols bgp system-as 65536 +set protocols bgp neighbor 192.168.2.1 shutdown +commit +exit +``` + +## Run operational commands + +In scripts, **always** prefix operational commands with `run`. + +```none +#!/bin/vbash +source /opt/vyatta/etc/functions/script-template +run show interfaces +exit +``` + +## Run commands remotely + +You can execute multiple **operational commands** on a remote VyOS system by +passing a script block over SSH. + +```none +ssh 192.0.2.1 'vbash -s' <<EOF +source /opt/vyatta/etc/functions/script-template +run show interfaces +exit +EOF +``` + +Example output: + +```none +Welcome to VyOS +Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down +Interface IP Address S/L Description +--------- ---------- --- ----------- +eth0 192.0.2.1/24 u/u +lo 127.0.0.1/8 u/u + ::1/128 +``` + +## Other script languages + +If you use a scripting language other than bash, configure your script to +output the relevant commands, and then source that output into a bash script. + +The following example demonstrates this two-step process: + +```python +#!/usr/bin/env python3 +print("delete firewall group address-group somehosts") +print("set firewall group address-group somehosts address '192.0.2.3'") +print("set firewall group address-group somehosts address '203.0.113.55'") +``` + +```none +#!/bin/vbash +source /opt/vyatta/etc/functions/script-template +configure +source <(/config/scripts/setfirewallgroup.py) +commit +``` + +## Execute configuration scripts + +In Linux, it is common practice to prefix system commands with `sudo`. + +In VyOS, if you prefix a script that modifies the configuration with `sudo` +(see the code snippet below), subsequent manual configuration changes fail with +the `Set failed` error. Recovery requires a system reboot. + +```none +sudo ./myscript.sh # Modifies config +configure +set ... # Any configuration parameter +``` + +To avoid this issue, run scripts under the `vyattacfg` group using the `sg` +command: + +```none +sg vyattacfg -c ./myscript.sh +``` + +To ensure the script is executed under the `vyattacfg` group, safeguard it as +follows: + +```none +if [ "$(id -g -n)" != 'vyattacfg' ] ; then + exec sg vyattacfg -c "/bin/vbash $(readlink -f $0) $@" +fi +``` + +## Executing pre-hooks/post-hooks scripts + +VyOS allows you to run custom scripts **before** and **after** each commit. + +Place your custom scripts in the following default directories: + +```none +/config/scripts/commit/pre-hooks.d - Directory with scripts that run before + each commit. + +/config/scripts/commit/post-hooks.d - Directory with scripts that run after + each commit. +``` + +Scripts run in alphabetical order. Filenames must consist only of ASCII letters +(upper and lowercase), digits (0-9), underscores (\_), and hyphens (-). No other +characters are allowed. + +:::{note} +Custom scripts are executed **without** root privileges. Prefix +specific commands with `sudo` in your script when required. +::: + +The following example shows the output after executing a post-hook script +that runs the `show interfaces` command: + +```none +vyos@vyos# set interfaces ethernet eth1 address 192.0.2.3/24 +vyos@vyos# commit +Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down +Interface IP Address S/L Description +--------- ---------- --- ----------- +eth0 198.51.100.10/24 u/u +eth1 192.0.2.3/24 u/u +eth2 - u/u +eth3 - u/u +lo 203.0.113.5/24 u/u +``` + +## Preconfig script on boot + +VyOS runs `/config/scripts/vyos-preconfig-bootup.script` at boot, **before** +the system configuration is applied. + +Use this script to apply **pre-configuration** workarounds for unresolved bugs +or enhancements not yet available in VyOS. + +The default script contains the following: + +```none +#!/bin/sh +# This script is executed at boot time before VyOS configuration is applied. +# Any modifications required to work around unfixed bugs or use +# services not available through the VyOS CLI system can be placed here. +``` + +## Postconfig script on boot + +VyOS runs `/config/scripts/vyos-postconfig-bootup.script` at boot, **after** +the system configuration is applied. + +Use this script to apply **post-configuration** workarounds for unresolved bugs +or enhancements not yet available in VyOS. + +The default script contains the following: + +```none +#!/bin/sh +# This script is executed at boot time after VyOS configuration is fully +# applied. Any modifications required to work around unfixed bugs or use +# services not available through the VyOS CLI system can be placed here. +``` + +:::{warning} +For configuration or upgrade management issues, modify this script +only as a last resort. Always try CLI-based solutions first. +::: diff --git a/docs/automation/md-index.md b/docs/automation/md-index.md new file mode 100644 index 00000000..62e60f84 --- /dev/null +++ b/docs/automation/md-index.md @@ -0,0 +1,16 @@ +# VyOS Automation + +```{toctree} +:maxdepth: 2 + +vyos-api +vyos-ansible +terraform/index +vyos-napalm +vyos-netmiko +vyos-salt +command-scripting +cloud-init +vyos-pyvyos +vyos-govyos +``` diff --git a/docs/automation/md-vyos-ansible.md b/docs/automation/md-vyos-ansible.md new file mode 100644 index 00000000..1ced72be --- /dev/null +++ b/docs/automation/md-vyos-ansible.md @@ -0,0 +1,99 @@ +--- +lastproofread: '2026-04-13' +--- + +(vyos-ansible)= + +# Ansible + +VyOS can be configured using Ansible. To use it, install the `ansible` +package and the `python3-paramiko` module. + +## Directory structure + +Arrange your Ansible project directory as follows: + +```none +. +├── ansible.cfg +├── files +│ └── id_rsa_docker.pub +├── hosts +└── main.yml +``` + +## File contents + +- `ansible.cfg` + +```none +[defaults] +host_key_checking = no +retry_files_enabled = False +ANSIBLE_INVENTORY_UNPARSED_FAILED = true +``` + +- `id_rsa_docker.pub` + +Contains only the SSH public key. + +```none +AAAAB3NzaC1yc2EAAAADAQABAAABAQCoDgfhQJuJRFWJijHn7ZinZ3NWp4hWVrt7HFcvn0kgtP/5PeCtMt +``` + +- `hosts` + +Defines the target VyOS devices and the connection parameters required to reach +them. + +```none +[vyos_hosts] +r11 ansible_ssh_host=192.0.2.11 + +[vyos_hosts:vars] +ansible_python_interpreter=/usr/bin/python3 +ansible_user=vyos +ansible_ssh_pass=vyos +ansible_network_os=vyos +ansible_connection=network_cli +``` + +- `main.yml` + +Defines the configuration tasks to be applied to the target VyOS devices. + +```none +--- + +- hosts: r11 + + connection: network_cli + gather_facts: 'no' + + tasks: + - name: Configure remote r11 + vyos_config: + lines: + - set system host-name r11 + - set system name-server 203.0.113.254 + - set service ssh disable-host-validation + - set system login user vyos authentication public-keys docker@work type ssh-rsa + - set system login user vyos authentication public-keys docker@work key "{{ lookup('file', 'id_rsa_docker.pub') }}" + - set system time-zone America/Los_Angeles + - set interfaces ethernet eth0 description WAN +``` + +## Run Ansible + +To apply the configuration, use the following command: + +```none +$ ansible-playbook -i hosts main.yml + +PLAY [r11] ************************************************************************************************** + +TASK [Configure remote r11] ********************************************************************************* + +PLAY RECAP ************************************************************************************************** +r11 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 +``` diff --git a/docs/automation/md-vyos-govyos.md b/docs/automation/md-vyos-govyos.md new file mode 100644 index 00000000..f1ad3e91 --- /dev/null +++ b/docs/automation/md-vyos-govyos.md @@ -0,0 +1,186 @@ +--- +lastproofread: '2026-04-14' +--- + +(vyos-govyos)= + +# Go-VyOS + +Go-VyOS is a Go library for configuring and managing VyOS devices through +their API. + +- [GitHub repository](https://github.com/ganawaj/go-vyos): Hosts the source + code. +- [Documentation](https://pkg.go.dev/github.com/ganawaj/go-vyos@v0.1.0/vyos): + Provides the complete API reference, including available types, functions, and + methods. + +## Installation + +To install Go-VyOS, run: + +```bash +go install "github.com/ganawaj/go-vyos/vyos" +``` + +## Getting started + +### Import and disable TLS verification + +```none +import "github.com/ganawaj/go-vyos/vyos" +client := vyos.NewClient(nil).WithToken("AUTH_KEY").WithURL("https://192.168.0.1").Insecure() +``` + +### Initialize a VyDevice object + +```none +import ( + "github.com/ganawaj/go-vyos/vyos" + "os" +) + +hostname := os.Getenv("VYDEVICE_HOSTNAME") +port := os.Getenv("VYDEVICE_PORT") +url := fmt.Sprintf("https://%s:%s", hostname, port) + +apikey := os.Getenv("VYDEVICE_APIKEY") +verify_ssl := os.Getenv("VYDEVICE_VERIFY_SSL") + +client := vyos.NewClient(nil).WithToken(apikey).WithURL(url) + +if verify_ssl == "false" { + client = client.Insecure() +} +``` + +## Use Go-VyOS + +### Configure, then set + +```none +out, resp, err := c.Conf.Set(ctx, "interfaces ethernet eth0 address 192.168.1.1/24") +if err != nil { + panic(fmt.Sprintf("Error: %v", err)) +} + +fmt.Println(out.Success) +``` + +### Show a single object value + +```none +out, resp, err := c.Show.Do(ctx, "interfaces dummy dum1 address") +if err != nil { + panic("Error: %v", err) +} + +fmt.Println(out.Success) +fmt.Printf("Data: %v\n", out.Data) +``` + +### Configure, then show object + +```none +out, resp, err := c.Conf.Get(ctx, "interfaces dummy dum1", nil) +if err != nil { + panic("Error: %v", err) +} + +fmt.Println(out.Success) +fmt.Printf("Data: %v\n", out.Data) +``` + +### Configure, then show multivalue object + +```none +options := RetrieveOptions{ + Multivalue: true, +} + +out, resp, err := c.Conf.Get(ctx, "interfaces dummy dum1", options) +if err != nil { + panic("Error: %v", err) +} + +fmt.Println(out.Success) +``` + +### Configure, then delete object + +```none +out, resp, err := c.Conf.Delete(ctx, "interfaces dummy dum1") +if err != nil { + panic("Error: %v", err) +} + +fmt.Println(out.Success) +``` + +### Configure, then save + +```none +out, resp, err := c.Conf.Save(ctx, "") + +if err != nil { + panic("Error: %v", err) +} + +fmt.Println(out.Success) +``` + +### Configure, then save file + +```none +out, resp, err := c.Conf.Save(ctx, "/config/test300.config") + +if err != nil { + panic("Error: %v", err) +} + +fmt.Println(out.Success) +``` + +### Show object + +```none +out, resp, err := c.Show.Do(ctx, "system image") +if err != nil { + panic("Error: %v", err) +} + +fmt.Println(out.Success) +fmt.Printf("Data: %v\n", out.Data) +``` + +### Generate object + +```none +out, resp, err := c.Generate.Do(ctx, "pki wireguard key-pair") +if err != nil { + panic("Error: %v", err) +} + +fmt.Println(out.Success) +fmt.Printf("Data: %v\n", out.Data) +``` + +### Reset object + +```none +out, resp, err := c.Reset.Do(ctx, "ip bgp 192.0.2.11") +if err != nil { + panic("Error: %v", err) +} + +fmt.Println(out.Success) +fmt.Printf("Data: %v\n", out.Data) +``` + +### Configure, then load file + +```none +out, resp, err := c.ConfigFile.Load(ctx, "/config/test300.config") +``` + +[go-vyos]: https://github.com/ganawaj/go-vyos diff --git a/docs/automation/md-vyos-napalm.md b/docs/automation/md-vyos-napalm.md new file mode 100644 index 00000000..87567593 --- /dev/null +++ b/docs/automation/md-vyos-napalm.md @@ -0,0 +1,152 @@ +--- +lastproofread: '2026-04-13' +--- + +(vyos-napalm)= + +# NAPALM VyOS driver + +VyOS can be configured using the [NAPALM VyOS driver], which enables you to +retrieve device data and apply configurations via SSH. + +:::{note} +The `napalm-vyos` module is currently in testing. +::: + +To use the NAPALM VyOS driver, install the following packages: + +```none +apt install python3-pip +pip3 install napalm +pip3 install napalm-vyos +``` + +## Retrieve device data + +The following script connects to a VyOS device, retrieves device facts and +the ARP table, and prints the output in JSON format. + +```none +#!/usr/bin/env python3 + +import json +from napalm import get_network_driver + +driver = get_network_driver('vyos') + +vyos_router = driver( + hostname="192.0.2.1", + username="vyos", + password="vyospass", + optional_args={"port": 22}, +) + +vyos_router.open() +output = vyos_router.get_facts() +print(json.dumps(output, indent=4)) + +output = vyos_router.get_arp_table() +print(json.dumps(output, indent=4)) + +vyos_router.close() +``` + +Output: + +```none +$ ./vyos-napalm.py +{ + "uptime": 7185, + "vendor": "VyOS", + "os_version": "1.3.0-rc5", + "serial_number": "", + "model": "Standard PC (Q35 + ICH9, 2009)", + "hostname": "r4-1.3", + "fqdn": "vyos.local", + "interface_list": [ + "eth0", + "eth1", + "eth2", + "lo", + "vtun10" + ] +} +[ + { + "interface": "eth1", + "mac": "52:54:00:b2:38:2c", + "ip": "192.0.2.2", + "age": 0.0 + }, + { + "interface": "eth0", + "mac": "52:54:00:a2:b9:5b", + "ip": "203.0.113.11", + "age": 0.0 + } +] +``` + +## Apply a configuration + +To apply a configuration using NAPALM VyOS driver, you will need a file with +configuration commands (`commands.conf`) and a script that executes and +commits them (`vyos-napalm.py`). + +- `commands.conf` + +```none +set service ssh disable-host-validation +set service ssh port '2222' +set system name-server '192.0.2.8' +set system name-server '203.0.113.8' +set interfaces ethernet eth1 description 'FOO' +``` + +- `vyos-napalm.py` + +```none +#!/usr/bin/env python3 + +from napalm import get_network_driver + +driver = get_network_driver('vyos') + +vyos_router = driver( + hostname="192.0.2.1", + username="vyos", + password="vyospass", + optional_args={"port": 22}, +) + +vyos_router.open() +vyos_router.load_merge_candidate(filename='commands.conf') +diffs = vyos_router.compare_config() + +if bool(diffs) == True: + print(diffs) + vyos_router.commit_config() +else: + print('No configuration changes to commit') + vyos_router.discard_config() + +vyos_router.close() +``` + +Output: + +```none +$./vyos-napalm.py +[edit interfaces ethernet eth1] ++description FOO +[edit service ssh] ++disable-host-validation ++port 2222 +[edit system] ++name-server 192.0.2.8 ++name-server 203.0.113.8 +[edit] +``` + +[napalm]: https://napalm.readthedocs.io/en/latest/base.html +[NAPALM VyOS driver]: https://github.com/napalm-automation-community/napalm-vyos diff --git a/docs/automation/md-vyos-netmiko.md b/docs/automation/md-vyos-netmiko.md new file mode 100644 index 00000000..2e947b6a --- /dev/null +++ b/docs/automation/md-vyos-netmiko.md @@ -0,0 +1,76 @@ +--- +lastproofread: '2026-04-13' +--- + +(vyos-netmiko)= + +# Netmiko + +VyOS can be configured using [Netmiko]. To use Netmiko, install the +`python3-netmiko` module. + +## Example + +The following script connects to a VyOS device, applies configuration changes, +commits them, and runs an operational mode command to verify the updated +configuration. + +```none +#!/usr/bin/env python3 + +from netmiko import ConnectHandler + +vyos_router = { + "device_type": "vyos", + "host": "192.0.2.1", + "username": "vyos", + "password": "vyospass", + "port": 22, + } + +net_connect = ConnectHandler(**vyos_router) + +config_commands = [ + 'set interfaces ethernet eth0 description WAN', + 'set interfaces ethernet eth1 description LAN', + ] + +# set configuration +output = net_connect.send_config_set(config_commands, exit_config_mode=False) +print(output) + +# commit configuration +output = net_connect.commit() +print(output) + +# operational mode commands +output = net_connect.send_command("run show interfaces") +print(output) +``` + +Output + +```none +$ ./vyos-netmiko.py +configure +set interfaces ethernet eth0 description WAN +[edit] +vyos@r4-1.5# set interfaces ethernet eth1 description LAN +[edit] +vyos@r4-1.5# +commit +[edit] +vyos@r4-1.5# +Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down +Interface IP Address S/L Description +--------- ---------- --- ----------- +eth0 203.0.113.1/24 u/u WAN +eth1 192.0.2.1/30 u/u LAN +eth2 - u/u +lo 127.0.0.1/8 u/u + ::1/128 +vtun10 10.10.0.1/24 u/u +[edit] +``` + +[netmiko]: https://github.com/ktbyers/netmiko diff --git a/docs/automation/md-vyos-pyvyos.md b/docs/automation/md-vyos-pyvyos.md new file mode 100644 index 00000000..2a9599d7 --- /dev/null +++ b/docs/automation/md-vyos-pyvyos.md @@ -0,0 +1,138 @@ +--- +lastproofread: '2026-04-14' +--- + +(vyos-pyvyos)= + +# PyVyOS + +PyVyOS is a Python library for configuring and managing VyOS devices through +their API. + +**Key resources:** + +- [Documentation](https://pyvyos.readthedocs.io/en/latest/): Provides + installation, configuration, and usage instructions. +- [GitHub repository](https://github.com/robertoberto/pyvyos): Hosts the + source code. +- [PyPI](https://pypi.org/project/pyvyos/): Hosts distribution packages for + installation via the Python package installer (`pip`). + +## Installation + +To install PyVyOS via `pip`, run: + +```bash +pip install pyvyos +``` + +## Getting started + +### Import and disable warnings for verify=false + +```none +import urllib3 +urllib3.disable_warnings() +``` + +### Use API response class + +```none +@dataclass +class ApiResponse: + status: int + request: dict + result: dict + error: str +``` + +### Initialize a VyDevice object + +```none +from dotenv import load_dotenv +load_dotenv() + +hostname = os.getenv('VYDEVICE_HOSTNAME') +apikey = os.getenv('VYDEVICE_APIKEY') +port = os.getenv('VYDEVICE_PORT') +protocol = os.getenv('VYDEVICE_PROTOCOL') +verify_ssl = os.getenv('VYDEVICE_VERIFY_SSL') + +verify = verify_ssl.lower() == "true" if verify_ssl else True + +device = VyDevice(hostname=hostname, apikey=apikey, port=port, protocol=protocol, verify=verify) +``` + +## Use PyVyOS + +### Configure, then set + +```none +response = device.configure_set(path=["interfaces", "ethernet", "eth0", "address", "192.168.1.1/24"]) +if not response.error: + print(response.result) +``` + +### Configure, then show a single object value + +```none +response = device.retrieve_return_values(path=["interfaces", "dummy", "dum1", "address"]) +print(response.result) +``` + +### Configure, then show object + +```none +response = device.retrieve_show_config(path=[]) +if not response.error: + print(response.result) +``` + +### Configure, then delete object + +```none +response = device.configure_delete(path=["interfaces", "dummy", "dum1"]) +``` + +### Configure, then save + +```none +response = device.config_file_save() +``` + +### Configure, then save file + +```none +response = device.config_file_save(file="/config/test300.config") +``` + +### Show object + +```none +response = device.show(path=["system", "image"]) +print(response.result) +``` + +### Generate object + +```none +randstring = ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(20)) +keyrand = f'/tmp/key_{randstring}' +response = device.generate(path=["ssh", "client-key", keyrand]) +``` + +### Reset object + +```none +response = device.reset(path=["conntrack-sync", "internal-cache"]) +if not response.error: + print(response.result) +``` + +### Configure, then load file + +```none +response = device.config_file_load(file="/config/test300.config") +``` + +[pyvyos]: https://github.com/robertoberto/pyvyos diff --git a/docs/automation/terraform/md-index.md b/docs/automation/terraform/md-index.md new file mode 100644 index 00000000..9f741c35 --- /dev/null +++ b/docs/automation/terraform/md-index.md @@ -0,0 +1,28 @@ +--- +lastproofread: '2026-03-23' +--- + +# VyOS Terraform + +VyOS supports development infrastructure via Terraform and provisioning +via Ansible. +Terraform allows you to automate the deployment of instances on a number of +cloud and virtual platforms. This section shows how to deploy VyOS on +multiple platforms: AWS, Microsoft Azure, Google Cloud Platform (GCP), +and VMware vSphere. +For more information, see the +official documentation for [Terraform] and [Ansible]. + +```{toctree} +:caption: Guides +:maxdepth: 1 + +terraformAWS +terraformAZ +terraformGoogle +terraformvSphere +``` + +[ansible]: https://docs.ansible.com +[install]: https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli +[terraform]: https://developer.hashicorp.com/terraform/intro |
