summaryrefslogtreecommitdiff
path: root/docs/automation
diff options
context:
space:
mode:
authorYuriy Andamasov <yuriy@vyos.io>2026-05-06 14:40:28 +0300
committerGitHub <noreply@github.com>2026-05-06 12:40:28 +0100
commit4b36114e053ee11d0cb264a1e4cfe4692d78f194 (patch)
treebe4ecc665eb3f1d556a37e768eed14989fec57b6 /docs/automation
parent21a554bd4f9156e41f1c73ba6b7223bb63b3a4ef (diff)
downloadvyos-documentation-4b36114e053ee11d0cb264a1e4cfe4692d78f194.tar.gz
vyos-documentation-4b36114e053ee11d0cb264a1e4cfe4692d78f194.zip
Add incremental RST-to-MyST swap mechanism (#1857)
* feat: add swap_sources.py for incremental RST-to-MyST migration Pre-build swap/restore script that renames md-{name}.md → {name}.md before Sphinx builds and restores after. Includes state tracking, exclude file generation, collision detection, and partial-failure rollback. 10 tests cover all specified behaviors plus rollback path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add import_myst.py for importing MyST files from myst/* branches Adds scripts/import_myst.py with import_page, git_show, list_myst_files, list_rst_files, and do_import. Imported files are written as md-{name}.md alongside existing RST files; importing is decoupled from swap activation. Adds tests/test_import_myst.py covering single-page write, identical-skip, warn-on-different-without-force, force-overwrite, and nested-path creation. All 5 tests pass on Python 3.9. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add MyST swap exclude patterns and directive config to conf.py 🤖 Generated by [robots](https://vyos.io) * feat: add swap-wrapped rendering targets to Makefile 🤖 Generated by [robots](https://vyos.io) * feat: add swap pre/post build hooks for ReadTheDocs 🤖 Generated by [robots](https://vyos.io) * feat: add empty _swap.txt, remove atexit from swap script The atexit handler in --swap mode caused immediate restore on process exit, breaking standalone usage. Makefile trap and RTD post_build handle restore reliably. 🤖 Generated by [robots](https://vyos.io) * feat: activate quick-start as MyST canary via swap mechanism Imports docs/md-quick-start.md from origin/myst/current and adds quick-start to docs/_swap.txt. Validates the swap pipeline end-to-end on one page: import_myst pulls the MD via git show, swap_sources renames md-quick-start.md to quick-start.md, sphinx-build renders quick-start.html with zero MD-specific warnings, and restore reverses the rename cleanly. 🤖 Generated by [robots](https://vyos.io) * 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) * fix: re-import 4 canary md-*.md files with xref label fixes Re-imports the dash-form-corrected versions of: - contributing/md-development.md (added (coding-guidelines)= anchor) - operation/md-upgrade-recovery.md (3 ref renames: how_it_works / cancelling_recovery -> dash form) - vpp/configuration/dataplane/md-buffers.md (vpp_config_dataplane_physmem -> vpp-config-dataplane-physmem) - vpp/configuration/dataplane/md-unix.md (vpp_config_dataplane_interface_rx_mode -> vpp-config-dataplane-interface-rx-mode) Source: origin/myst/current commit 59fbe3ea. Verified locally: clean swap-build no longer reports any of the 5 target labels (1 of 6 — vpp-config-hugepages — remains because system.md isn't in the canary swap list; that anchor lives there). 🤖 Generated by [robots](https://vyos.io) * fix: re-add 4 canary md-*.md files deleted by 242b334a Commit 242b334a accidentally staged deletions instead of modifications because the working tree had unprefixed *.md files left over from an incomplete swap-restore cycle. Re-imports the same 4 files from origin/myst/current with the xref label fixes applied: - contributing/md-development.md — (coding-guidelines)= anchor - operation/md-upgrade-recovery.md — how_it_works → how-it-works, cancelling_recovery → cancelling-recovery - vpp/configuration/dataplane/md-buffers.md — vpp_config_dataplane_physmem → vpp-config-dataplane-physmem - vpp/configuration/dataplane/md-unix.md — vpp_config_dataplane_interface_rx_mode → vpp-config-dataplane-interface-rx-mode Source: origin/myst/current commit 59fbe3ea. 🤖 Generated by [robots](https://vyos.io) * fix: resolve remaining xref label gaps in swap-active build Three small additions clear the cross-reference warnings tied to underscore-vs-dash label form mismatches and the vpp-config-hugepages reference that previously needed system.md in the canary set. - system.rst: add .. _vpp-config-hugepages: alongside the existing underscore label so memory.md references resolve regardless of whether system.md is swap-active. - md-lcp.md: add (vpp_config_dataplane_lcp_ignore-kernel-routes)= alongside dash form (carries upstream from myst/current 079fa786). - md-memory.md: add (vpp_config_dataplane_memory)= alongside dash form (also from myst/current 079fa786). Local clean swap-build with 106 canaries: before: 305 warnings, 8 undefined-label entries in our scope after: 300 warnings, 0 undefined-label entries in our scope Remaining undefined-label warnings (release-notes, prepare_commit) are in documentation.rst and unrelated to the canary swap mechanism. 🤖 Generated by [robots](https://vyos.io) * fix: re-add md-lcp.md and md-memory.md (deleted by 870c9e7e) Same disaster pattern as 242b334a: a swap-restore cycle left unprefixed *.md files in the working tree, and the subsequent git add staged deletions instead of modifications. Restoring the two affected md-*.md files from origin/myst/current 079fa786 (which has the dual underscore+dash anchors needed for the swap-active build). 🤖 Generated by [robots](https://vyos.io) * feat: expand canaries to 114; refresh 3 with cfgcmd body fix Adds 8 new visual-validated canaries from the post-cfgcmd-fix BackstopJS run (2026-04-29): - configuration/policy/as-path-list - configuration/policy/community-list - configuration/policy/extcommunity-list - configuration/policy/large-community-list - configuration/policy/local-route - configuration/policy/prefix-list - configuration/service/salt-minion - configuration/system/updates Refreshes 3 existing canaries whose MD content changed via the cfgcmd/opcmd single-line body fix on myst/current fc19ab5c: - configuration/firewall/global-options - configuration/firewall/groups - configuration/policy/route All 11 sourced from origin/myst/current. Net: 106 -> 114 canaries. 🤖 Generated by [robots](https://vyos.io) * fix: re-import md-cloud-init.md (block 3 fix from myst/current) 🤖 Generated by [robots](https://vyos.io) * feat(swap): import .md files and webp transition from myst/current Selective import from origin/myst/current (cf9c9b34): - Add/update 255 .md files (full MyST conversion plus webp ref updates) - Delete 175 PNG/JPG from docs/_static/images (webp twins already present) - Delete 5 autotest topology.png (webp twins already present) Preserved on swap (untouched): - All .rst files (incremental swap pattern) - conf.py, _ext/, _include/*.txt, .gitignore - 115 canary md-*.md files - 7 superpowers/specs/*.md design docs - Logos vyos-logo.png / vyos-logo-icon.png (referenced by conf.py) 🤖 Generated by [robots](https://vyos.io) * chore(swap): remove canary md-*.md files and docs/superpowers - Remove 115 canary md-*.md files (incremental swap helpers no longer needed) - Remove 8 files under docs/superpowers (project planning/design docs that shouldn't ship in the documentation tree) 🤖 Generated by [robots](https://vyos.io) * docs: address Copilot review feedback on imported MyST pages Fix issues flagged by Copilot review on PR #1857 (the same content lives in myst/current as the canonical source): Real bugs: - site-2-site-cisco.md: replace curly quote (U+2019) with ASCII apostrophe - rsa-keys.md: fix typo "key-pair nam>>" → "key-pair name>" - vmware.md: lowercase admonition directive (:::{NOTE} → :::{note}) - vpp/configuration/nat/index.md: remove blank line inside {include} fence Grammar: - vpp/configuration/interfaces/loopback.md: "bounded" → "bound" - vpp/configuration/sflow.md: "VyOS support" → "VyOS supports" - vpp/requirements.md: "bypass" → "bypasses" - vpp/configuration/dataplane/interface.md: "configures" → "configure" CI linter (IP addresses): - nmp.md: wrap 8.8.8.8 example with stop/start_vyoslinter - lac-lns.md: wrap LNS config block (contains 8.8.8.8) - wan-load-balancing.md: wrap whole file (illustrative non-RFC IPs) - policy/examples.md: replace 192.0.1.1 with RFC 5737 192.0.2.1 🤖 Generated by [robots](https://vyos.io) * fix(swap): address Copilot review feedback on swap infrastructure Category D — drop obsolete canary mechanism settings: - conf.py: remove '**/md-*.md' from exclude_patterns (no canaries left) - Makefile: replace malformed '*/_build/*' with '$(BUILDDIR)/**' and drop the '*/md-*' ignore (canary files no longer exist) Category C — script robustness: - import_myst.py: * list_myst_files() now raises SystemExit on git ls-tree failure instead of silently returning [] (would have masked typo'd --source refs) * list_rst_files() skips _build/ when scanning for .rst stems * import_page() rejects stems containing '..' or absolute paths and re-checks that the resolved destination stays under docs_dir * --dry-run uses a separate "would_import" counter; summary line now distinguishes dry-run from actual imports - swap_sources.py: * parse_swap_list() reads with explicit encoding='utf-8' * do_restore() validates state file version + entry shape before renaming files; raises with actionable message on corruption * State file reads/writes use explicit encoding='utf-8' throughout _swap.txt: - Wrap long comment line to satisfy 80-character doc-linter limit 🤖 Generated by [robots](https://vyos.io) * refactor(swap): rename imported .md files to md- prefix for swap mechanism Restore the canary file naming convention that swap_sources.py expects: the imported MyST pages now live as docs/<dir>/md-<name>.md alongside the existing docs/<dir>/<name>.rst, so swap_sources.py --swap can rename them into place at build time. - 254 .md files renamed (every page with a matching .rst counterpart) - 2 MyST-only pages left at their final names (no .rst exists, no swap needed): docs/copyright.md, docs/automation/terraform/terraformvyos.md All 114 stems listed in docs/_swap.txt now have a corresponding md-<name>.md source file ready to swap in. 🤖 Generated by [robots](https://vyos.io) * docs: address CodeRabbit review feedback on imported MyST pages Fix issues flagged by CodeRabbit on PR #1857. All issues are pre-existing in the upstream RST docs and inherited by the MyST conversion. Real bugs: - inter-vrf-routing-vrf-lite.md: invalid IPv6 next-hop "2001:db8::*" → "2001:db8::1" - ipsec-pa-route-based.md: vendor mislabel "Cisco" → "Palo Alto" (header on line 39 and "Monitoring on Cisco side" section heading) - bgp-ipv6-unnumbered.md: AS number mismatch between configuration and verification output for both routers (Router A: 65020 → 64496; Router B: 65021 → 64499) - qos.md: class 30 used "match ADDRESS20" instead of ADDRESS30 — broke the documented pattern (classes 10/20/30 → ADDRESS10/20/30) Security: - OpenVPN_with_LDAP.md: redact full PEM private key material from the three "set pki ... private key '...'" lines and from the embedded OpenVPN client <key> block; replace with <REDACTED> / ...REDACTED... placeholders. Public certificates retained. 🤖 Generated by [robots](https://vyos.io) * feat(swap): default to serving MyST for all swapped pages Replace the previously-curated 114-stem _swap.txt with the full set of 254 imported md-prefixed pages, so MD is served by default at build time. To revert any specific page back to RST, remove its stem from _swap.txt (or comment it out). 🤖 Generated by [robots](https://vyos.io) * fix(ext): handle RST fallback in CmdInclude when _renderer absent `cmdincludemd` is in `myst_fence_as_directive`, so MyST routes fence blocks through `render_fence → render_restructuredtext → MockRSTParser`. In that path `self.state` is a plain docutils Body with no `_renderer`, crashing the build. Fall back to `nested_parse` when `_renderer` is unavailable so the directive works in both MyST and RST/MockRSTParser contexts. 🤖 Generated by [robots](https://vyos.io) * feat(conf): copy .md sources into HTML output for plain-text serving Adds a build-finished hook that mirrors every .md file from the Sphinx source tree into the HTML output directory verbatim, making unrendered MyST sources accessible alongside HTML renders at the same URL path. 🤖 Generated by [robots](https://vyos.io) * docs: address review feedback from PR #1857 Fix conversion artifacts, typos, grammar errors, and technical inaccuracies flagged by automated code review (Copilot + CodeRabbit). Infrastructure: add root-level md-*.md exclusion to conf.py, fix sphinx-autobuild ignore globs in Makefile. Content: fix curly quotes, invalid Go panic() calls, shell quoting in cURL examples, incorrect firewall command paths, typos across 22 documentation files, remove duplicate sections. 🤖 Generated by [robots](https://vyos.io) --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'docs/automation')
-rw-r--r--docs/automation/md-cloud-init.md378
-rw-r--r--docs/automation/md-command-scripting.md225
-rw-r--r--docs/automation/md-index.md16
-rw-r--r--docs/automation/md-vyos-ansible.md101
-rw-r--r--docs/automation/md-vyos-api.md587
-rw-r--r--docs/automation/md-vyos-govyos.md197
-rw-r--r--docs/automation/md-vyos-napalm.md152
-rw-r--r--docs/automation/md-vyos-netmiko.md76
-rw-r--r--docs/automation/md-vyos-pyvyos.md149
-rw-r--r--docs/automation/md-vyos-salt.md211
-rw-r--r--docs/automation/terraform/md-index.md29
-rw-r--r--docs/automation/terraform/md-terraformAWS.md548
-rw-r--r--docs/automation/terraform/md-terraformAZ.md501
-rw-r--r--docs/automation/terraform/md-terraformGoogle.md703
-rw-r--r--docs/automation/terraform/md-terraformvSphere.md388
-rw-r--r--docs/automation/terraform/terraformvyos.md44
16 files changed, 4305 insertions, 0 deletions
diff --git a/docs/automation/md-cloud-init.md b/docs/automation/md-cloud-init.md
new file mode 100644
index 00000000..b6350b54
--- /dev/null
+++ b/docs/automation/md-cloud-init.md
@@ -0,0 +1,378 @@
+---
+lastproofread: '2026-04-13'
+---
+
+(cloud-init)=
+
+# VyOS cloud-init
+
+VyOS instances in cloud and virtualized environments are initialized using the
+industry-standard `cloud-init`. Through `cloud-init`, VyOS injects SSH
+keys, configures network settings, and applies custom configurations during the
+initial instance boot.
+
+## Configuration sources
+
+VyOS `cloud-init` obtains configuration data from the following sources:
+
+- `meta-data`: Instance-specific details provided by the cloud platform or
+ hypervisor. In some cloud environments, this data is available via an HTTP
+ endpoint at `http://169.254.169.254`.
+- `network configuration`: Network settings such as IP addresses, routes, and
+ DNS (only available on certain cloud and virtualization platforms).
+- `user-data`: User-supplied CLI configuration commands.
+
+## User-data
+
+Major cloud providers support injecting `user-data` as plain text or base64
+encoding text during initial instance boot. As `user-data` has a strict size
+limit of \~16384 bytes, long configuration command lists can be compressed using
+`gzip`.
+
+The recommended method for configuring VyOS instances via `user-data` is to
+use the `cloud-config` syntax described below.
+
+## Cloud-config modules
+
+By default, VyOS enables only two `cloud-config` modules:
+
+- `write_files`: Inserts user-provided files such as encryption keys,
+ certificates, or `config.boot` into the filesystem during the initial
+ instance boot. See [Cloud-init-write_files] for file syntax and file format
+ requirements.
+- `vyos_userdata`: Executes user-provided CLI configuration commands during
+ the initial instance boot.
+
+The files to insert and the CLI commands to execute must be provided in a
+`cloud-config` YAML file.
+
+## Cloud-config file format
+
+`cloud-config` files are written in YAML and must begin with the
+`#cloud-config` line. Only `vyos_config_commands` and `write_files` are
+supported as top-level keys. The use of these keys is described in the
+following two sections.
+
+## Vyos_config_commands key
+
+Use the `vyos_config_commands` key to define configuration commands for
+initializing your VyOS instance. Commands must follow the set-style syntax
+and can include both `set` and `delete` statements.
+
+Syntax requirements:
+
+- Place one command per line.
+- Enclose values in single quotes.
+- Avoid single quotes within commands or values.
+
+Applying commands from `cloud-config` overrides both settings configured via
+`meta-data` and default VyOS settings. After commands are applied,
+`cloud-init` automatically performs `commit` and `save`.
+
+The following is an example of a `cloud-config` file:
+
+```yaml
+#cloud-config
+vyos_config_commands:
+ - set system host-name 'vyos-prod-ashburn'
+ - set service ntp server 1.pool.ntp.org
+ - set service ntp server 2.pool.ntp.org
+ - delete interfaces ethernet eth1 address 'dhcp'
+ - set interfaces ethernet eth1 address '192.0.2.247/24'
+ - set protocols static route 198.51.100.0/24 next-hop '192.0.2.1'
+```
+
+
+### Instance defaults/fallbacks
+
+If no external configuration data is provided, VyOS applies the following
+defaults:
+
+- **SSH:** port 22.
+- **Credentials:** `vyos`/`vyos`.
+- **Networking:** DHCP is enabled on the first Ethernet interface.
+
+All defaults can be overridden via `user-data` configurations.
+
+## Write_files key
+
+VyOS allows you to run custom scripts during the initial instance boot to
+execute operational, configuration, and standard Linux commands.
+
+Use the `write_files` key to insert these scripts into the
+`/opt/vyatta/etc/config/scripts/` directory.
+
+Depending on when your commands need to run, use one of the following paths:
+
+- `/opt/vyatta/etc/config/scripts/vyos-preconfig-bootup.script`: Commands
+ defined here are executed before the system configuration is applied.
+- `/opt/vyatta/etc/config/scripts/vyos-postconfig-bootup.script`: Commands
+ defined here are executed after the system configuration is applied.
+
+In both cases, commands are executed with `root` privileges.
+
+:::{note}
+Use the `/opt/vyatta/etc/config/` path instead of `/config/scripts/` as
+referenced in the {ref}`command-scripting` section. The `/config/scripts/`
+directory is not mounted when the `write_files` module runs.
+:::
+
+The following example shows how to use `write_files` to execute an
+operational command **after** the initial configuration is complete:
+
+```yaml
+#cloud-config
+write_files:
+ - path: /opt/vyatta/etc/config/scripts/vyos-postconfig-bootup.script
+ owner: root:vyattacfg
+ permissions: '0775'
+ content: |
+ #!/bin/vbash
+ source /opt/vyatta/etc/functions/script-template
+ filename=/tmp/bgp_status_`date +"%Y_%m_%d_%I_%M_%p"`.log
+ run show ip bgp summary >> $filename
+```
+
+You can combine standard Linux commands to fetch data and VyOS configuration
+commands (like `set` and `commit`) in the same script.
+
+The following example sets the `hostname` based on the instance identifier
+obtained from the EC2 Instance Metadata Service (IMDS).
+
+```yaml
+#cloud-config
+write_files:
+ - path: /opt/vyatta/etc/config/scripts/vyos-postconfig-bootup.script
+ owner: root:vyattacfg
+ permissions: '0775'
+ content: |
+ #!/bin/vbash
+ source /opt/vyatta/etc/functions/script-template
+ hostname=`curl -s http://169.254.169.254/latest/meta-data/instance-id`
+ configure
+ set system host-name $hostname
+ commit
+ exit
+```
+
+
+## NoCloud
+
+Injecting configuration data is not limited to cloud platforms. The NoCloud
+data source allows you to inject `user-data` and `meta-data` on
+virtualization platforms such as VMware, Hyper-V, and KVM.
+
+The simplest way to use the NoCloud data source is to create a `seed.iso`
+file and attach it to the virtual machine as a CD drive. The volume must be
+formatted as a VFAT or ISO 9660 file system with the label `cidata` or
+`CIDATA`.
+
+Create text files named `user-data` and `meta-data`. On Linux-based
+systems, use the `mkisofs` utility to create the `seed.iso` file. The
+following syntax adds these files to the ISO 9660 file system:
+
+```none
+mkisofs -joliet -rock -volid "cidata" -output seed.iso meta-data user-data
+```
+
+Once generated, attach the `seed.iso` file to your virtual machine. The
+following example shows how to attach the file as a CD drive using KVM:
+
+```none
+$ virt-install -n vyos_r1 \
+ --ram 4096 \
+ --vcpus 2 \
+ --cdrom seed.iso \
+ --os-type linux \
+ --os-variant debian10 \
+ --network network=default \
+ --graphics vnc \
+ --hvm \
+ --virt-type kvm \
+ --disk path=/var/lib/libvirt/images/vyos_kvm.qcow2,bus=virtio \
+ --import \
+ --noautoconsole
+```
+
+For more information on the NoCloud data source, visit the [NoCloud] page in
+the `cloud-init` documentation.
+
+## Troubleshooting
+
+If your configuration does not apply as expected, follow these troubleshooting
+steps:
+
+1. **Validate your YAML**: Ensure your `cloud-config` file follows proper
+ YAML syntax. Online resources such as [YAML Lint](https://www.yamllint.com/)
+ provide simple validation tools.
+2. **Check the logs**: `cloud-init` writes logs to `/var/log/cloud-init.log`.
+ Filter for VyOS-specific entries using:
+
+```none
+sudo grep vyos /var/log/cloud-init.log
+```
+
+
+## Cloud-init on Proxmox
+
+Before you begin, review the `cloud-init` [network-config-docs] to
+understand how to import user and network configuration data.
+
+Key considerations:
+
+- Define VyOS configuration commands in the `user-data` file.
+- Avoid including network configuration data in the `user-data` file.
+- If no network configuration data is provided, the DHCP client is enabled on
+ the first interface. This happens at the OS level and is not reflected in the
+ VyOS CLI.
+
+The following example shows how to disable the DHCP client on `eth0` to
+address this behavior.
+
+In this example:
+
+- **Proxmox IP address**: `192.168.0.253/24`.
+- **Storage**: The `local` volume is mounted at `/var/lib/vz` and contains
+ all content types, including snippets.
+
+The goal is to remove the default DHCP client from the first interface and
+apply a custom configuration during the initial instance boot using
+`cloud-init`.
+
+### Generate .qcow2 image
+
+First, generate a VyOS `.qcow2` image with `cloud-init` support from the
+[vyos-vm-images] repository:
+
+1. Clone the `vyos-vm-images` repository and comment out the `download-iso`
+ role in `qemu.yml`.
+2. Download your preferred VyOS `.iso` file and save it as `/tmp/vyos.iso`.
+3. Generate the `.qcow2` image (using a 10G disk size for this example):
+
+```sh
+sudo ansible-playbook qemu.yml -e disk_size=10 \
+ -e iso_local=/tmp/vyos.iso -e grub_console=serial -e vyos_version=1.5.0 \
+ -e cloud_init=true -e cloud_init_ds=NoCloud
+```
+
+This generates your new image at `/tmp/vyos-1.5.0-cloud-init-10G-qemu.qcow2`.
+
+4. Copy the resulting image to the Proxmox server:
+
+```sh
+sudo scp /tmp/vyos-1.5.0-cloud-init-10G-qemu.qcow2 root@192.168.0.253:/tmp/
+```
+
+
+### Prepare cloud-init files
+
+Create the following files on your Proxmox server to proceed with this setup:
+
+- `user-data`: Contains VyOS configuration commands.
+- `network-config`: Disables the DHCP client on the first interface.
+- `meta-data`: An empty file (required by `cloud-init`).
+
+All files must be placed in the `/tmp/` directory.
+
+Follow these steps to create the required files:
+
+1. Navigate to the `/tmp/` directory:
+
+ ```sh
+ cd /tmp/
+ ```
+
+2. Create the `user-data` file. Begin the file with `#cloud-config` and
+ include VyOS configuration commands.
+
+ ```none
+ #cloud-config
+ vyos_config_commands:
+ - set system host-name 'vyos-BRAS'
+ - set service ntp server '1.pool.ntp.org'
+ - set service ntp server '2.pool.ntp.org'
+ - delete interfaces ethernet eth0 address 'dhcp'
+ - set interfaces ethernet eth0 address '198.51.100.2/30'
+ - set interfaces ethernet eth0 description 'WAN - ISP01'
+ - set interfaces ethernet eth1 address '192.168.25.1/24'
+ - set interfaces ethernet eth1 description 'Coming through VLAN 25'
+ - set interfaces ethernet eth2 address '192.168.26.1/24'
+ - set interfaces ethernet eth2 description 'Coming through VLAN 26'
+ - set protocols static route 0.0.0.0/0 next-hop '198.51.100.1'
+ ```
+
+3. Create the `network-config` file. Include the following:
+
+ ```none
+ version: 2
+ ethernets:
+ eth0:
+ dhcp4: false
+ dhcp6: false
+ ```
+
+4. Create the required empty `meta-data` file.
+
+### Create seed.iso
+
+Once you have created the necessary files, generate the `seed.iso` image and
+mount it as a CD drive to the new VM.
+
+```sh
+mkisofs -joliet -rock -volid "cidata" -output seed.iso meta-data \
+user-data network-config
+```
+
+:::{note}
+Be careful while copying and pasting the above commands. Double quotes may need
+to be corrected.
+:::
+
+### Create the VM
+
+Note that the following settings apply to this particular example and may
+require adjustment for other setups:
+
+- **VM ID**: `555`.
+- **VM and .iso file storage**: The local volume (`directory` type,
+ mounted at `/var/lib/vz`).
+- **VM resources**: Can be modified as needed.
+
+The `seed.iso` file was previously created in the `/tmp/` directory. Move
+it to `/var/lib/vz/template/iso`:
+
+```sh
+mv /tmp/seed.iso /var/lib/vz/template/iso/
+```
+
+On the Proxmox server:
+
+```none
+## Create VM, import disk and define boot order
+qm create 555 --name vyos-1.5.0-cloudinit --memory 1024 --net0 virtio,bridge=vmbr0
+qm importdisk 555 vyos-1.5.0-cloud-init-10G-qemu.qcow2 local
+qm set 555 --virtio0 local:555/vm-555-disk-0.raw
+qm set 555 --boot order=virtio0
+
+## Import seed.iso for cloud init
+qm set 555 --ide2 media=cdrom,file=local:iso/seed.iso
+
+## Since this server has 1 nic, lets add network intefaces (vlan 25 and 26)
+qm set 555 --net1 virtio,bridge=vmbr0,firewall=1,tag=25
+qm set 555 --net2 virtio,bridge=vmbr0,firewall=1,tag=26
+```
+
+
+### Power on and verify the VM
+
+Power on the VM using the CLI or GUI. After it boots, verify the configuration.
+
+### References
+
+- Cloud-init [network-config-docs].
+- Proxmox [Cloud-init-Support].
+[cloud-init-support]: <https://pve.proxmox.com/pve-docs/pve-admin-guide.html#qm_cloud_init>
+[cloud-init-write_files]: https://cloudinit.readthedocs.io/en/latest/topics/examples.html#writing-out-arbitrary-files
+[network-config-docs]: https://cloudinit.readthedocs.io/en/latest/topics/network-config.html
+[nocloud]: https://cloudinit.readthedocs.io/en/latest/reference/datasources/nocloud.html
+[vyos-vm-images]: https://github.com/vyos/vyos-vm-images
diff --git a/docs/automation/md-command-scripting.md b/docs/automation/md-command-scripting.md
new file mode 100644
index 00000000..7e736152
--- /dev/null
+++ b/docs/automation/md-command-scripting.md
@@ -0,0 +1,225 @@
+---
+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..8e94b251
--- /dev/null
+++ b/docs/automation/md-vyos-ansible.md
@@ -0,0 +1,101 @@
+---
+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-api.md b/docs/automation/md-vyos-api.md
new file mode 100644
index 00000000..66e8250c
--- /dev/null
+++ b/docs/automation/md-vyos-api.md
@@ -0,0 +1,587 @@
+---
+lastproofread: '2026-04-13'
+---
+
+(vyosapi)=
+
+# VyOS API
+
+For instructions on configuring and enabling the API, see {ref}`http-api`.
+
+## Authentication
+
+All endpoints, except one, accept HTTP POST requests. The API key must be
+provided as the `key` field in the form data. The only public endpoint
+accepts HTTP GET requests and supports optional query parameters.
+
+Below are examples of API requests in cURL and Python. All other code examples
+in this documentation use cURL.
+
+```none
+curl --location --request POST 'https://vyos/retrieve' \
+--form data='{"op": "showConfig", "path": []}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+```
+
+```python
+import requests
+url = "https://vyos/retrieve"
+payload={'data': '{"op": "showConfig", "path": []}',
+ 'key': 'MY-HTTPS-API-PLAINTEXT-KEY'
+ }
+headers = {}
+response = requests.request("POST", url, headers=headers, data=payload)
+print(response.text)
+```
+
+
+## API endpoints
+
+### /info
+
+This is the only API endpoint that does not require authentication and can be
+accessed by anonymous users. The info endpoint returns general system
+information, including the VyOS version, system hostname, and a welcome banner.
+
+This endpoint accepts **only** HTTP GET requests.
+
+```none
+curl --location --request GET 'https://vyos/info'
+
+response
+{
+ "success": true,
+ "data": {
+ "version": "1.5-rolling",
+ "hostname": "vyos",
+ "banner": "Welcome to VyOS"
+ },
+ "error": null
+}
+```
+
+**Query parameters**
+
+This endpoint accepts two optional query parameters, version and hostname. Each
+parameter accepts values convertible to Boolean (e.g., `yes/no`, `1/0`, or
+`true/false`) to control the inclusion of related fields in the response.
+
+If no query parameters are provided, both parameters default to `true`.
+
+```none
+curl --location --request GET 'https://vyos/info?version=1&hostname=1'
+
+response {
+ "success": true,
+ "data": {
+ "version": "1.5-rolling",
+ "hostname": "vyos",
+ "banner": "Welcome to VyOS"
+ },
+ "error": null
+}
+```
+
+If either parameter is set to a value corresponding to false, its field is
+returned as an empty string in the response:
+
+```none
+curl --location --request GET 'https://vyos/info?version=0&hostname=1'
+
+response {
+ "success": true,
+ "data": {
+ "version": "",
+ "hostname": "vyos",
+ "banner": "Welcome to VyOS"
+ },
+ "error": null
+}
+```
+
+You do not need to specify both parameters if you want to hide only one. Any
+missing query parameter defaults to true.
+
+```none
+curl --location --request GET 'https://vyos/info?hostname=no'
+
+response {
+ "success": true,
+ "data": {
+ "version": "1.5-rolling",
+ "hostname": "",
+ "banner": "Welcome to VyOS"
+ },
+ "error": null
+}
+```
+
+Note that while you can disable output for both `hostname` and `version`,
+the `banner` is always included in the response.
+
+:::{Important}
+The endpoint accepts **ONLY** `hostname` and `version` query
+parameters. Including any other parameters results in an HTTP 400 Bad Request.
+:::
+
+```none
+curl --location --request GET \
+ 'https://192.168.56.119/info?hostname=1&url=https://evilsite.com'
+
+response {
+ "success": false,
+ "error": "{'type': 'extra_forbidden', 'loc': ('query', 'url'), 'msg': 'Extra inputs are not permitted', 'input': 'https://evilsite.com'}",
+ "data": null
+}
+```
+
+Values passed to the query string are validated to ensure they are strictly
+Boolean. Other data types are not accepted.
+
+```none
+curl --location --request GET 'https://vyos/info?hostname=1; eval"sudo rm -rf /"'
+
+response
+{
+ "success": false,
+ "error": "{'type': 'bool_parsing', 'loc': ('query', 'hostname'), 'msg': 'Input should be a valid boolean, unable to interpret input', 'input': '1; eval \"sudo rm -rf /\"'}",
+ "data": null
+}
+```
+
+
+### /retrieve
+
+The `/retrieve` endpoint returns either specific parts or the entire
+configuration.
+
+To retrieve the entire configuration, pass an empty list to the `path` field.
+
+```none
+curl --location --request POST 'https://vyos/retrieve' \
+--form data='{"op": "showConfig", "path": []}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+
+response (shortened)
+{
+ "success": true,
+ "data": {
+ "interfaces": {
+ "ethernet": {
+ "eth0": {
+ "address": "dhcp",
+ "duplex": "auto",
+ "hw-id": "50:00:00:01:00:00",
+ "speed": "auto"
+ },
+ "eth1": {
+ "duplex": "auto",
+ "hw-id": "50:00:00:01:00:01",
+ "speed": "auto"
+ ...
+ },
+ "error": null
+}
+```
+
+To retrieve a specific configuration part, such as `system syslog`, specify
+the desired path.
+
+```none
+curl -k --location --request POST 'https://vyos/retrieve' \
+--form data='{"op": "showConfig", "path": ["system", "syslog"]}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+
+response:
+{
+ "success": true,
+ "data": {
+ "global": {
+ "facility": {
+ "all": {
+ "level": "info"
+ },
+ "protocols": {
+ "level": "debug"
+ }
+ }
+ }
+ },
+ "error": null
+}
+```
+
+If you only need the value of a multi-valued node, use the `returnValues`
+operation.
+
+For example, to get the addresses of a `dum0` interface:
+
+```none
+curl -k --location --request POST 'https://vyos/retrieve' \
+--form data='{"op": "returnValues", "path": ["interfaces","dummy","dum0","address"]}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+response:
+{
+ "success": true,
+ "data": [
+ "10.10.10.10/24",
+ "10.10.10.11/24",
+ "10.10.10.12/24"
+ ],
+ "error": null
+}
+```
+
+To check whether a configuration path exists, use the `exists` operation. It
+returns `true` for an existing path:
+
+```none
+curl -k --location --request POST 'https://vyos/retrieve' \
+--form data='{"op": "exists", "path": ["service","https","api"]}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+response:
+{
+ "success": true,
+ "data": true,
+ "error": null
+}
+```
+
+It returns `false` for a non-existing path:
+
+```none
+curl -k --location --request POST 'https://vyos/retrieve' \
+--form data='{"op": "exists", "path": ["service","non","existent","path"]}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+response:
+{
+ "success": true,
+ "data": false,
+ "error": null
+}
+```
+
+
+### /reset
+
+The `/reset` endpoint runs the `reset` command.
+
+```none
+curl --location --request POST 'https://vyos/reset' \
+--form data='{"op": "reset", "path": ["ip", "bgp", "192.0.2.11"]}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+response:
+{
+ "success": true,
+ "data": "",
+ "error": null
+}
+```
+
+
+### /reboot
+
+To initiate a reboot, use the `/reboot` endpoint.
+
+```none
+curl --location --request POST 'https://vyos/reboot' \
+--form data='{"op": "reboot", "path": ["now"]}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+response:
+{
+ "success": true,
+ "data": "",
+ "error": null
+}
+```
+
+
+### /poweroff
+
+To power off the system, use the `/poweroff` endpoint.
+
+```none
+curl --location --request POST 'https://vyos/poweroff' \
+--form data='{"op": "poweroff", "path": ["now"]}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+response:
+{
+ "success": true,
+ "data": "",
+ "error": null
+}
+```
+
+
+### /image
+
+To add or delete an image, use the `/image` endpoint.
+
+To add an image:
+
+```none
+curl -k --location --request POST 'https://vyos/image' \
+--form data='{"op": "add", "url": "https://downloads.vyos.io/rolling/current/amd64/vyos-rolling-latest.iso"}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+response (shortened):
+{
+ "success": true,
+ "data": "Trying to fetch ISO file from https://downloads.vyos.io/rolling-latest.iso\n
+ ...
+ Setting up grub configuration...\nDone.\n",
+ "error": null
+}
+```
+
+To delete an image, for example `1.3-rolling-202006070117`:
+
+```none
+curl -k --location --request POST 'https://vyos/image' \
+--form data='{"op": "delete", "name": "1.3-rolling-202006070117"}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+response:
+{
+ "success": true,
+ "data": "Deleting the \"1.3-rolling-202006070117\" image...\nDone\n",
+ "error": null
+}
+```
+
+
+### /show
+
+The `/show` endpoint runs operational mode commands and returns the resulting
+output.
+
+For example, to show the installed images:
+
+```none
+curl -k --location --request POST 'https://vyos/show' \
+--form data='{"op": "show", "path": ["system", "image"]}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+response:
+{
+ "success": true,
+ "data": "The system currently has the following image(s) installed:\n\n
+ 1: 1.4-rolling-202102280559 (default boot)\n
+ 2: 1.4-rolling-202102230218\n
+ 3: 1.3-beta-202102210443\n\n",
+ "error": null
+}
+```
+
+
+### /generate
+
+The `/generate` endpoint runs a `generate` command.
+
+```none
+curl -k --location --request POST 'https://vyos/generate' \
+--form data='{"op": "generate", "path": ["pki", "wireguard", "key-pair"]}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+response:
+{
+ "success": true,
+ "data": "Private key: CFZR2eyhoVZwk4n3JFPMJx3E145f1EYgDM+ubytXYVY=\n
+ Public key: jjtpPT8ycI1Q0bNtrWuxAkO4k88Xwzg5VHV9xGZ58lU=\n\n",
+ "error": null
+}
+```
+
+
+### /configure
+
+The `/configure` endpoint accepts `set`, `delete`, and `comment` commands.
+
+To apply a `set` command:
+
+```none
+curl -k --location --request POST 'https://vyos/configure' \
+--form data='{"op": "set", "path": ["interfaces", "dummy", "dum1", "address", "10.11.0.1/32"]}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+response:
+{
+ "success": true,
+ "data": null,
+ "error": null
+}
+```
+
+To apply a `delete` command:
+
+```none
+curl -k --location --request POST 'https://vyos/configure' \
+--form data='{"op": "delete", "path": ["interfaces", "dummy", "dum1", "address", "10.11.0.1/32"]}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+response:
+{
+ "success": true,
+ "data": null,
+ "error": null
+}
+```
+
+The API processes each request in a session and commits it. For components such
+as DHCP and PPPoE servers, IPsec, VXLAN, and other tunnels, VyOS requires the
+entire configuration block for a commit.
+
+The endpoint can process multiple commands if you pass them as a list to
+the `data` field.
+
+```none
+curl -k --location --request POST 'https://vyos/configure' \
+--form data='[{"op": "set","path":["interfaces","vxlan","vxlan1","remote","203.0.113.99"]}, {"op": "set","path":["interfaces","vxlan","vxlan1","vni","1"]}]' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+response:
+{
+ "success": true,
+ "data": null,
+ "error": null
+}
+```
+
+
+### /config-file
+
+The `/config-file` endpoint allows you to save, load, or merge a
+configuration.
+
+If you do not specify a file during the `save` operation, the configuration
+is automatically saved to `/config/config.boot`.
+
+```none
+curl -k --location --request POST 'https://vyos/config-file' \
+--form data='{"op": "save"}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+response:
+{
+ "success": true,
+ "data": "Saving configuration to '/config/config.boot'...\nDone\n",
+ "error": null
+}
+```
+
+To save a running configuration to a file:
+
+```none
+curl -k --location --request POST 'https://vyos/config-file' \
+--form data='{"op": "save", "file": "/config/test.config"}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+response:
+{
+ "success": true,
+ "data": "Saving configuration to '/config/test.config'...\nDone\n",
+ "error": null
+}
+```
+
+To load a configuration file:
+
+```none
+curl -k --location --request POST 'https://vyos/config-file' \
+--form data='{"op": "load", "file": "/config/test.config"}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+response:
+{
+ "success": true,
+ "data": null,
+ "error": null
+}
+```
+
+To merge a configuration file:
+
+```none
+curl -k --location --request POST 'https://vyos/config-file' \
+--form data='{"op": "merge", "file": "/config/test.config"}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+response:
+{
+ "success": true,
+ "data": null,
+ "error": null
+}
+```
+
+For both `load` and `merge` operations, you can pass a string in the
+request body. For example:
+
+```none
+curl -k --location --request POST 'https://vyos/config-file' \
+--form data='{"op": "merge", "string": "interfaces {\nethernet eth1 {\naddress \"192.168.2.137/24\"\ndescription \"test\"\n}\n}\n"}' \
+--form key='MY-HTTPS-API-PLAINTEXT-KEY'
+
+response:
+{
+ "success": true,
+ "data": null,
+ "error": null
+}
+```
+
+
+## Commit-confirm
+
+For the previous two endpoints, a `commit` command is executed automatically
+after a successful request operation (`set`, `delete`, `load`, `merge`,
+or a list of `set` and `delete` operations).
+
+Alternatively, you can initiate a `commit-confirm`. Include the
+`confirm_time` field in your request and set it to an integer greater than
+`0`.
+
+The following example uses the JSON format for brevity, though the standard
+form data format is equally valid:
+
+```none
+curl -k -X POST -d '{"key": "MY-HTTPS-API-PLAINTEXT-KEY", "op": "merge", "string": "interfaces {\nethernet eth1 {\naddress \"192.168.137.1/24\"\ndescription \"internal\"\n}\n}\n", "confirm_time": 1}' https://vyos/config-file
+
+response:
+{
+ "success": true,
+ "data": "Initialized commit-confirm; 1 minutes to confirm before reload\n",
+ "error": null
+}
+```
+
+If not confirmed within the specified time, the committed changes will be
+reverted. To confirm and keep the changes:
+
+```none
+curl -k -X POST -d '{"key": "MY-HTTPS-API-PLAINTEXT-KEY", "op": "confirm"}' https://vyos/config-file
+
+response:
+{
+ "success": true,
+ "data": "Reload timer stopped\n",
+ "error": null
+}
+```
+
+If the commit is not confirmed, the revert behavior is controlled by:
+
+```none
+vyos@vyos# set system config-management commit-confirm action
+Possible completions:
+ reload Reload previous configuration if not confirmed
+ reboot Reboot to saved configuration if not confirmed (default)
+```
diff --git a/docs/automation/md-vyos-govyos.md b/docs/automation/md-vyos-govyos.md
new file mode 100644
index 00000000..c1d0b24a
--- /dev/null
+++ b/docs/automation/md-vyos-govyos.md
@@ -0,0 +1,197 @@
+---
+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(fmt.Sprintf("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(fmt.Sprintf("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(fmt.Sprintf("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(fmt.Sprintf("Error: %v", err))
+}
+
+fmt.Println(out.Success)
+```
+
+
+### Configure, then save
+
+```none
+out, resp, err := c.Conf.Save(ctx, "")
+
+if err != nil {
+ panic(fmt.Sprintf("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(fmt.Sprintf("Error: %v", err))
+}
+
+fmt.Println(out.Success)
+```
+
+
+### Show object
+
+```none
+out, resp, err := c.Show.Do(ctx, "system image")
+if err != nil {
+ panic(fmt.Sprintf("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(fmt.Sprintf("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(fmt.Sprintf("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")
+```
diff --git a/docs/automation/md-vyos-napalm.md b/docs/automation/md-vyos-napalm.md
new file mode 100644
index 00000000..d656c7c2
--- /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 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..1a6a09fd
--- /dev/null
+++ b/docs/automation/md-vyos-pyvyos.md
@@ -0,0 +1,149 @@
+---
+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")
+```
diff --git a/docs/automation/md-vyos-salt.md b/docs/automation/md-vyos-salt.md
new file mode 100644
index 00000000..7b306cb8
--- /dev/null
+++ b/docs/automation/md-vyos-salt.md
@@ -0,0 +1,211 @@
+---
+lastproofread: '2023-01-16'
+---
+
+(vyos-salt)=
+
+```{include} /_include/need_improvement.txt
+```
+
+
+# Salt
+
+VyOS supports op-mode and configuration via [salt].
+
+Without proxy it requires VyOS minion configuration
+and supports op-mode data:
+
+```none
+set service salt-minion id 'r14'
+set service salt-minion master '192.0.2.250'
+```
+
+Check salt-keys on the salt master
+
+```none
+/ # salt-key --list-all
+Accepted Keys:
+r11
+Denied Keys:
+Unaccepted Keys:
+r14
+Rejected Keys:
+```
+
+Accept minion key
+
+```none
+/ # salt-key --accept r14
+The following keys are going to be accepted:
+Unaccepted Keys:
+r14
+Proceed? [n/Y] y
+Key for minion r14 accepted.
+```
+
+Check that salt master can communicate with minions
+
+```none
+/ # salt '*' test.ping
+r14:
+ True
+r11:
+ True
+```
+
+At this step we can get some op-mode information from VyOS nodes:
+
+```none
+/ # salt '*' network.interface eth0
+r11:
+ |_
+ ----------
+ address:
+ 192.0.2.11
+ broadcast:
+ 192.0.2.255
+ label:
+ eth0
+ netmask:
+ 255.255.255.0
+r14:
+ |_
+ ----------
+ address:
+ 192.0.2.14
+ broadcast:
+ 192.0.2.255
+ label:
+ eth0
+ netmask:
+ 255.255.255.0
+
+
+/ # salt r14 network.arp
+r14:
+ ----------
+ aa:bb:cc:dd:f3:db:
+ 192.0.2.1
+ aa:bb:cc:dd:2e:80:
+ 203.0.113.1
+```
+
+## Netmiko-proxy
+
+It is possible to configure VyOS via [netmiko] proxy module.
+It requires a minion with installed packet `python3-netmiko` module
+who has a connection to VyOS nodes. Salt-minion have to communicate
+with salt master
+
+### Configuration
+
+Salt master configuration:
+
+```none
+/ # cat /etc/salt/master
+file_roots:
+ base:
+ - /srv/salt/states
+
+pillar_roots:
+ base:
+ - /srv/salt/pillars
+```
+
+Structure of /srv/salt:
+
+```none
+/ # tree /srv/salt/
+/srv/salt/
+|___ pillars
+| |__ r11-proxy.sls
+| |__ top.sls
+|___ states
+ |__ commands.txt
+```
+
+top.sls
+
+```none
+/ # cat /srv/salt/pillars/top.sls
+base:
+ r11-proxy:
+ - r11-proxy
+```
+
+r11-proxy.sls Includes parameters for connecting to salt-proxy minion
+
+```none
+/ # cat /srv/salt/pillars/r11-proxy.sls
+proxy:
+ proxytype: netmiko # how to connect to proxy minion, change it
+ device_type: vyos #
+ host: 192.0.2.250
+ username: user
+ password: secret_passwd
+```
+
+commands.txt
+
+```none
+/ # cat /srv/salt/states/commands.txt
+set interfaces ethernet eth0 description 'WAN'
+set interfaces ethernet eth1 description 'LAN'
+```
+
+Check that proxy minion is alive:
+
+```none
+/ # salt r11-proxy test.ping
+r11-proxy:
+ True
+/ #
+```
+
+### Examples
+
+Example of op-mode:
+
+```none
+/ # salt r11-proxy netmiko.send_command 'show interfaces ethernet eth0 brief' host=192.0.2.14 device_type=vyos username=vyos password=vyos
+r11-proxy:
+ Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down
+ Interface IP Address S/L Description
+ --------- ---------- --- -----------
+ eth0 192.0.2.14/24 u/u Upstream
+/ #
+```
+
+Example of configuration:
+
+```none
+/ # salt r11-proxy netmiko.send_config config_commands=['set interfaces ethernet eth0 description Link_to_WAN'] commit=True host=192.0.2.14 device_type=vyos username=vyos password=vyos
+r11-proxy:
+ configure
+ set interfaces ethernet eth0 description Link_to_WAN
+ [edit]
+ vyos@r14# commit
+ [edit]
+ vyos@r14#
+/ #
+```
+
+Example of configuration commands from the file
+"/srv/salt/states/commands.txt"
+
+```none
+/ # salt r11-proxy netmiko.send_config config_file=salt://commands.txt commit=True host=192.0.2.11 device_type=vyos username=vyos password=vyos
+r11-proxy:
+ configure
+ set interfaces ethernet eth0 description 'WAN'
+ [edit]
+ vyos@r1# set interfaces ethernet eth1 description 'LAN'
+ [edit]
+ vyos@r1# commit
+ [edit]
+ vyos@r1#
+/ #
+```
+
+[netmiko]: <https://docs.saltproject.io/en/latest/ref/modules/all/salt.modules.netmiko_mod.html#module-salt.modules.netmiko_mod>
+[salt]: https://docs.saltproject.io/en/latest/contents.html
diff --git a/docs/automation/terraform/md-index.md b/docs/automation/terraform/md-index.md
new file mode 100644
index 00000000..dc787db1
--- /dev/null
+++ b/docs/automation/terraform/md-index.md
@@ -0,0 +1,29 @@
+---
+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
+
+terraformvyos
+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
diff --git a/docs/automation/terraform/md-terraformAWS.md b/docs/automation/terraform/md-terraformAWS.md
new file mode 100644
index 00000000..488e7926
--- /dev/null
+++ b/docs/automation/terraform/md-terraformAWS.md
@@ -0,0 +1,548 @@
+---
+lastproofread: '2026-03-16'
+---
+
+(terraformaws)=
+
+# Deploy VyOS on AWS with Terraform and Ansible
+
+You can use Terraform to quickly deploy VyOS-based infrastructure
+on AWS and remove infrastructure when it's no longer needed.
+Additionally, you can use Ansible for provisioning.
+
+```{eval-rst}
+.. image:: /_static/images/aws.webp
+ :width: 50%
+ :align: center
+ :alt: Network Topology Diagram
+```
+
+On this page you'll learn how to:
+- Create the necessary files for Terraform and Ansible.
+- Use Terraform to create a single instance on AWS and use Ansible for
+ provisioning.
+
+## Prepare to deploy VyOS with Terraform on AWS
+
+To create a single instance and install your configuration using
+Terraform, Ansible, and AWS, follow these steps:
+
+### AWS
+
+1. Create an account with AWS and get your `access_key` and `secret_key`.
+2. Create a key [pair] and download your `.pem` key.
+
+```{eval-rst}
+.. image:: /_static/images/keypairs.webp
+ :width: 50%
+ :align: center
+ :alt: Network Topology Diagram
+```
+
+3. Create a security [group] for the new VyOS instance and open all traffic.
+
+```{eval-rst}
+.. image:: /_static/images/sg.webp
+ :width: 50%
+ :align: center
+ :alt: Network Topology Diagram
+```
+
+```{eval-rst}
+.. image:: /_static/images/traffic.webp
+ :width: 50%
+ :align: center
+ :alt: Network Topology Diagram
+```
+
+
+### Terraform
+
+```{eval-rst}
+1. Create an UNIX or Windows instance.
+
+2. Download and install
+ `Terraform <https://developer.hashicorp.com/terraform/install>`__.
+
+3. Create a folder, for example ``/root/awsterraform``:
+
+ .. code-block:: none
+
+ mkdir /root/awsterraform
+
+.. stop_vyoslinter
+
+4. Copy all files into your Terraform project
+ (``vyos.tf``, ``var.tf``, ``terraform.tfvars``, ``version.tf``).
+ See `Structure of files in Terraform for AWS <#structure-of-files-in-terraform-for-aws>`__ for more details.
+
+.. start_vyoslinter
+
+5. Run the following commands:
+
+.. code-block:: none
+
+ cd /<your folder>
+ terraform init
+```
+
+### Ansible
+
+```{eval-rst}
+1. Create a UNIX instance whenever you need.
+
+2. Download and install Ansible
+
+3. Create a folder, for example ``/root/aws/``.
+
+.. stop_vyoslinter
+
+4. Copy all files into your Ansible project
+ (``ansible.cfg``, ``instance.yml``,
+ ``mykey.pem``, and ``all``).
+ See `Structure of files in Ansible for AWS <#structure-of-files-in-ansible-for-aws>`__ for more details.
+ You can obtain ``mykey.pem`` by creating a key `pair <https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/create-key-pairs.html>`__ in AWS and
+ downloading your ``.pem`` key.
+```
+
+### Deploy with Terraform
+
+Run the following commands on your Terraform instance:
+
+```none
+cd /<your folder>
+terraform plan
+terraform apply
+yes
+```
+
+## Create an AWS instance and check its configuration
+
+```none
+root@localhost:~/awsterraform# terraform apply
+
+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:
+
+ # aws_instance.myVyOSec2 will be created
+ + resource "aws_instance" "myVyOSec2" {
+ + ami = "ami-************62c2d"
+ + arn = (known after apply)
+ + associate_public_ip_address = (known after apply)
+ + availability_zone = (known after apply)
+ + cpu_core_count = (known after apply)
+ + cpu_threads_per_core = (known after apply)
+ + disable_api_stop = (known after apply)
+ + disable_api_termination = (known after apply)
+ + ebs_optimized = (known after apply)
+ + get_password_data = false
+ + host_id = (known after apply)
+ + host_resource_group_arn = (known after apply)
+ + iam_instance_profile = (known after apply)
+ + id = (known after apply)
+ + instance_initiated_shutdown_behavior = (known after apply)
+ + instance_lifecycle = (known after apply)
+ + instance_state = (known after apply)
+ + instance_type = "t2.micro"
+ + ipv6_address_count = (known after apply)
+ + ipv6_addresses = (known after apply)
+ + key_name = "awsterraform"
+ + monitoring = (known after apply)
+ + outpost_arn = (known after apply)
+ + password_data = (known after apply)
+ + placement_group = (known after apply)
+ + placement_partition_number = (known after apply)
+ + primary_network_interface_id = (known after apply)
+ + private_dns = (known after apply)
+ + private_ip = (known after apply)
+ + public_dns = (known after apply)
+ + public_ip = (known after apply)
+ + secondary_private_ips = (known after apply)
+ + security_groups = [
+ + "awsterraformsg",
+ ]
+ + source_dest_check = true
+ + spot_instance_request_id = (known after apply)
+ + subnet_id = (known after apply)
+ + tags = {
+ + "name" = "VyOS System"
+ }
+ + tags_all = {
+ + "name" = "VyOS System"
+ }
+ + tenancy = (known after apply)
+ + user_data = (known after apply)
+ + user_data_base64 = (known after apply)
+ + user_data_replace_on_change = false
+ + vpc_security_group_ids = (known after apply)
+ }
+
+ # local_file.ip will be created
+ + resource "local_file" "ip" {
+ + content = (known after apply)
+ + content_base64sha256 = (known after apply)
+ + content_base64sha512 = (known after apply)
+ + content_md5 = (known after apply)
+ + content_sha1 = (known after apply)
+ + content_sha256 = (known after apply)
+ + content_sha512 = (known after apply)
+ + directory_permission = "0777"
+ + file_permission = "0777"
+ + filename = "ip.txt"
+ + id = (known after apply)
+ }
+
+ # null_resource.SSHconnection1 will be created
+ + resource "null_resource" "SSHconnection1" {
+ + id = (known after apply)
+ }
+
+ # null_resource.SSHconnection2 will be created
+ + resource "null_resource" "SSHconnection2" {
+ + id = (known after apply)
+ }
+
+Plan: 4 to add, 0 to change, 0 to destroy.
+
+Changes to Outputs:
+ + my_IP = (known after apply)
+
+Do you want to perform these actions?
+ Terraform will perform the actions described above.
+ Only 'yes' will be accepted to approve.
+
+ Enter a value: yes
+
+aws_instance.myVyOSec2: Creating...
+aws_instance.myVyOSec2: Still creating... [10s elapsed]
+aws_instance.myVyOSec2: Still creating... [20s elapsed]
+aws_instance.myVyOSec2: Still creating... [30s elapsed]
+aws_instance.myVyOSec2: Still creating... [40s elapsed]
+aws_instance.myVyOSec2: Creation complete after 44s [id=i-09edfca15aac2fe0a]
+null_resource.SSHconnection1: Creating...
+null_resource.SSHconnection2: Creating...
+null_resource.SSHconnection1: Provisioning with 'file'...
+null_resource.SSHconnection2: Provisioning with 'remote-exec'...
+null_resource.SSHconnection2 (remote-exec): Connecting to remote host via SSH...
+null_resource.SSHconnection2 (remote-exec): Host: 10.217.80.104
+null_resource.SSHconnection2 (remote-exec): User: root
+null_resource.SSHconnection2 (remote-exec): Password: true
+null_resource.SSHconnection2 (remote-exec): Private key: false
+null_resource.SSHconnection2 (remote-exec): Certificate: false
+null_resource.SSHconnection2 (remote-exec): SSH Agent: false
+null_resource.SSHconnection2 (remote-exec): Checking Host Key: false
+null_resource.SSHconnection2 (remote-exec): Target Platform: unix
+local_file.ip: Creating...
+local_file.ip: Creation complete after 0s [id=e8e91f2e24579cd28b92e2d152c0c24c3bf4b52c]
+null_resource.SSHconnection2 (remote-exec): Connected!
+null_resource.SSHconnection1: Creation complete after 0s [id=7070868940858935600]
+
+null_resource.SSHconnection2 (remote-exec): PLAY [integration of terraform and ansible] ************************************
+
+null_resource.SSHconnection2 (remote-exec): TASK [Wait 300 seconds, but only start checking after 60 seconds] **************
+null_resource.SSHconnection2: Still creating... [10s elapsed]
+null_resource.SSHconnection2: Still creating... [20s elapsed]
+null_resource.SSHconnection2: Still creating... [30s elapsed]
+null_resource.SSHconnection2: Still creating... [40s elapsed]
+null_resource.SSHconnection2: Still creating... [50s elapsed]
+null_resource.SSHconnection2: Still creating... [1m0s elapsed]
+null_resource.SSHconnection2 (remote-exec): ok: [54.xxx.xxx.xxx]
+
+null_resource.SSHconnection2 (remote-exec): TASK [Configure general settings for the vyos hosts group] *********************
+null_resource.SSHconnection2: Still creating... [1m10s elapsed]
+null_resource.SSHconnection2 (remote-exec): changed: [54.xxx.xxx.xxx]
+
+null_resource.SSHconnection2 (remote-exec): PLAY RECAP *********************************************************************
+null_resource.SSHconnection2 (remote-exec): 54.xxx.xxx.xxx : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
+
+null_resource.SSHconnection2: Creation complete after 1m16s [id=4902256962410024771]
+
+Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
+
+Outputs:
+
+my_IP = "54.xxx.xxx.xxx"
+```
+
+After running all the commands, your VyOS instance is deployed on
+AWS with your specified configuration.
+To delete the instance, type the following command:
+
+```none
+terraform destroy
+```
+
+## Troubleshooting
+
+1. If Ansible doesn't connect via SSH to your AWS instance, verify that
+ your SSH key is in the path `/root/aws/`. You might need to
+ increase the timeout in `instance.yml` from 300 seconds to 500
+ seconds or more, depending on your location. Make sure that the
+ security group allows access to the instance.
+2. If Terraform doesn't connect via SSH to your Ansible instance,
+ verify the correct login and password in the `VyOS.tf` file.
+
+```{eval-rst}
+ .. code-block:: none
+
+ connection {
+ type = "ssh"
+ user = "root" # open root access using login and password on your Ansible
+ password = var.password # check password in the file terraform.tfvars isn't empty
+ host = var.host # check the correct IP address of your Ansible host
+ }
+```
+
+Make sure Ansible can ping from Terraform.
+
+## Structure of files in Terraform for AWS
+
+```none
+.
+├── vyos.tf # The main script
+├── var.tf # The file of all variables in "vyos.tf"
+├── versions.tf # File for the changing version of Terraform.
+└── terraform.tfvars # The value of all variables (passwords, login, ip adresses and so on)
+```
+
+## File contents of Terraform for AWS
+
+`vyos.tf`
+
+```none
+##############################################################################
+# Build a VyOS VM from the Marketplace.
+# Find the necessary AMI image_ in AWS.
+#
+# The vyos.tf script uses default values (you can change them as
+# needed)
+# AWS Region = "us-east-1"
+# AMI = "standard AMI of VyOS from AWS Marketplace"
+# Size of VM = "t2.micro"
+# AWS Region = "us-east-1"
+# After deploying the AWS instance and getting an IP address, the IP address is copied into the file
+#"ip.txt" and copied to the Ansible node for provisioning.
+##############################################################################
+
+provider "aws" {
+ access_key = var.access
+ secret_key = var.secret
+ region = var.region
+}
+
+variable "region" {
+ default = "us-east-1"
+ description = "AWS Region"
+}
+
+variable "ami" {
+ default = "ami-**************3b3" # ami image please enter your details
+ description = "Amazon Machine Image ID for VyOS"
+}
+
+variable "type" {
+ default = "t2.micro"
+ description = "Size of VM"
+}
+
+# my resource for VyOS
+
+resource "aws_instance" "myVyOSec2" {
+ ami = var.ami
+ key_name = "awsterraform" # Please enter your details from 1.2 of Preparation steps for deploying VyOS on AWS
+ security_groups = ["awsterraformsg"] # Please enter your details from 1.3 of Preparation steps for deploying VyOS on AWS
+ instance_type = var.type
+ tags = {
+ name = "VyOS System"
+ }
+}
+
+##############################################################################
+# Specific variable (to getting type "terraform plan"):
+# aws_instance.myVyOSec2.public_ip - the information about public IP address
+# of our instance, needs for provisioning and SSH connection from Ansible
+##############################################################################
+
+output "my_IP"{
+value = aws_instance.myVyOSec2.public_ip
+}
+
+##############################################################################
+# The IP address of the AWS instance is copied to the ip.txt file
+# on the local Terraform system. The ip.txt file contains the public
+# IP address in the format: xxx.xxx.xxx.xxx
+##############################################################################
+
+resource "local_file" "ip" {
+ content = aws_instance.myVyOSec2.public_ip
+ filename = "ip.txt"
+}
+
+#connecting to the Ansible control node using SSH connection
+
+##############################################################################
+# The "SSHconnection1" and "SSHconnection2" steps retrieve ip.txt
+# from the Terraform node and run the Ansible playbook remotely.
+##############################################################################
+
+resource "null_resource" "SSHconnection1" {
+depends_on = [aws_instance.myVyOSec2]
+connection {
+ type = "ssh"
+ user = "root"
+ password = var.password
+ host = var.host
+}
+
+# Copy the ip.txt file to the Ansible control node from the local
+# system
+ provisioner "file" {
+ source = "ip.txt"
+ destination = "/root/aws/ip.txt" # The folder of your Ansible project
+ }
+}
+
+resource "null_resource" "SSHconnection2" {
+depends_on = [aws_instance.myVyOSec2]
+connection {
+ type = "ssh"
+ user = "root"
+ password = var.password
+ host = var.host
+}
+# Run Ansible playbook on remote Linux OS
+provisioner "remote-exec" {
+ inline = [
+ "cd /root/aws/",
+ "ansible-playbook instance.yml" # more detailed in "File contents of Ansible for AWS"
+]
+}
+}
+```
+
+`var.tf`
+
+```none
+variable "password" {
+ description = "pass for Ansible"
+ type = string
+ sensitive = true
+}
+variable "host"{
+ description = "The IP of my Ansible"
+ type = string
+}
+variable "access" {
+ description = "my access_key for AWS"
+ type = string
+ sensitive = true
+}
+variable "secret" {
+ description = "my secret_key for AWS"
+ type = string
+ sensitive = true
+}
+```
+
+`versions.tf`
+
+```none
+ terraform {
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = "~> 5.0"
+ }
+ }
+}
+```
+
+`terraform.tfvars`
+
+```none
+password = "" # password for Ansible SSH
+host = "" # IP of my Ansible
+access = "" # access_key for AWS
+secret = "" # secret_key for AWS
+```
+
+## Structure of files in Ansible for AWS
+
+```none
+.
+├── group_vars
+ └── all
+├── ansible.cfg
+├── mykey.pem
+└── instance.yml
+```
+
+## File contents of Ansible for AWS
+
+`ansible.cfg`
+
+```none
+[defaults]
+inventory = /root/aws/ip.txt
+host_key_checking= False
+private_key_file = /root/aws/awsterraform.pem # check the name
+remote_user=vyos
+```
+
+`mykey.pem`
+
+```none
+Copy your key.pem from AWS
+```
+
+`instance.yml`
+
+```none
+##############################################################################
+# About tasks:
+# "Wait 300 seconds, but only start checking after 60 seconds" -
+# attempts SSH connection every 60 seconds until 300 seconds
+# "Configure general settings for the VyOS hosts group" -
+# provisions the AWS VyOS node
+# Add all necessary VyOS commands under the "lines:" block
+##############################################################################
+
+- name: integration of terraform and ansible
+ hosts: all
+ gather_facts: 'no'
+
+ tasks:
+
+ - name: "Wait 300 seconds, but only start checking after 60 seconds"
+ wait_for_connection:
+ delay: 60
+ timeout: 300
+
+ - name: "Configure general settings for the VyOS hosts group"
+ vyos_config:
+ lines:
+ - set system name-server xxx.xxx.xxx.xxx
+ save:
+ true
+```
+
+`group_vars/all`
+
+```none
+ansible_connection: ansible.netcommon.network_cli
+ansible_network_os: vyos.vyos.vyos
+ansible_user: vyos
+```
+
+
+## Source files on GitHub
+
+All files related to deploying VyOS on AWS with Terraform and Ansible
+can be found in the [vyos-automation] repository.
+
+[group]: https://docs.aws.amazon.com/cli/latest/userguide/cli-services-ec2-sg.html
+[pair]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/create-key-pairs.html
+[vyos-automation]: <https://github.com/vyos/vyos-automation/tree/main/TerraformCloud/AWS_terraform_ansible_single_vyos_instance-main>
diff --git a/docs/automation/terraform/md-terraformAZ.md b/docs/automation/terraform/md-terraformAZ.md
new file mode 100644
index 00000000..b1b8fac0
--- /dev/null
+++ b/docs/automation/terraform/md-terraformAZ.md
@@ -0,0 +1,501 @@
+---
+lastproofread: '2026-03-19'
+---
+
+(terraformaz)=
+
+# Deploy VyOS on Microsoft Azure with Terraform and Ansible
+
+You can use Terraform to quickly deploy VyOS-based infrastructure
+on Microsoft Azure (hereafter referred to as *Azure*) and remove
+infrastructure when it's no longer needed.
+Additionally, you can use Ansible for provisioning.
+
+On this page you'll learn how to:
+
+- Create the necessary files for Terraform and Ansible.
+- Use Terraform to create a single instance on Azure and use Ansible for
+ provisioning.
+
+## Prepare to deploy VyOS with Terraform on Azure
+
+To create a single instance and install your configuration using
+Terraform, Ansible, and Azure, follow these steps:
+
+### Azure
+
+- Create an [Azure account](https://azure.microsoft.com/).
+
+### Terraform
+
+```{eval-rst}
+1. Create an UNIX or Windows instance.
+
+2. Download and install
+ `Terraform <https://developer.hashicorp.com/terraform/install>`__.
+
+3. Create the folder for example ``/root/azvyos/``.
+
+.. code-block:: none
+
+ mkdir /root/azvyos
+
+.. stop_vyoslinter
+
+4. Copy all files into your Terraform project "/root/azvyos"
+ (``vyos.tf``, ``var.tf``, ``terraform.tfvars``). For more details, see
+ `Structure of files in Terraform for Azure <#structure-of-files-in-terraform-for-azure>`_.
+
+.. start_vyoslinter
+
+5. Log in to Azure using the command:
+
+ .. code-block:: none
+
+ az login
+
+6. Run the following commands to initialize Terraform:
+
+ .. code-block:: none
+
+ cd /<your folder>
+ terraform init
+```
+
+
+### Ansible
+
+1. Create an UNIX instance either locally or in the cloud.
+
+2. Download and install Ansible
+
+3. Create a folder, for example `/root/az/`.
+
+4. Copy all files into your Ansible project `/root/az/` (`ansible.cfg`,
+ `instance.yml`, `all`). For more details, see
+ [Structure of files in Ansible for Azure](#structure-of-files-in-ansible-for-azure)
+
+### Deploy with Terraform
+
+Run the following commands on your Terraform instance:
+
+```none
+cd /<your folder>
+terraform plan
+terraform apply
+yes
+```
+
+After executing all the commands, your VyOS instance is deployed to
+Azure with your configuration.
+If you need to delete the instance, run the following command:
+
+```none
+terraform destroy
+```
+
+
+## Structure of files in Terraform for Azure
+
+```none
+.
+├── vyos.tf # The main script
+├── var.tf # File for the Terraform version.
+└── terraform.tfvars # Values for all variables (passwords,
+ # login, IP addresses, etc.)
+```
+
+
+## File contents of Terraform for Azure
+
+`vyos.tf`
+
+```none
+##############################################################################
+# HashiCorp Guide to Using Terraform on Azure
+# This Terraform configuration will create the following:
+# Resource group with a virtual network and subnet
+# A VyOS server without SSH key (only login+password)
+##############################################################################
+
+# Choose a provider
+
+provider "azurerm" {
+ features {}
+}
+
+# Create a resource group. In Azure, every resource belongs to a
+# resource group.
+
+resource "azurerm_resource_group" "azure_vyos" {
+ name = "${var.resource_group}"
+ location = "${var.location}"
+}
+
+# The next resource is a Virtual Network.
+
+resource "azurerm_virtual_network" "vnet" {
+ name = "${var.virtual_network_name}"
+ location = "${var.location}"
+ address_space = ["${var.address_space}"]
+ resource_group_name = "${var.resource_group}"
+}
+
+# Build a subnet to run your VMs.
+
+resource "azurerm_subnet" "subnet" {
+ name = "${var.prefix}subnet"
+ virtual_network_name = "${azurerm_virtual_network.vnet.name}"
+ resource_group_name = "${var.resource_group}"
+ address_prefixes = ["${var.subnet_prefix}"]
+}
+
+##############################################################################
+# Build a VyOS VM from the Marketplace.
+# To find the necessary image, use the command:
+#
+# az vm image list --offer vyos --all
+#
+# Now that you have a network, you can deploy a VyOS server.
+# An Azure Virtual Machine has several components. In this example,
+# you build a security group, a network interface, a public IP
+# address, a storage account, and finally the VM itself. Terraform
+# handles all the dependencies automatically, and each resource is
+# named with user-defined variables.
+##############################################################################
+
+
+# Security group to allow inbound access on port 22 (SSH)
+
+resource "azurerm_network_security_group" "vyos-sg" {
+ name = "${var.prefix}-sg"
+ location = "${var.location}"
+ resource_group_name = "${var.resource_group}"
+
+ security_rule {
+ name = "SSH"
+ priority = 100
+ direction = "Inbound"
+ access = "Allow"
+ protocol = "Tcp"
+ source_port_range = "*"
+ destination_port_range = "22"
+ source_address_prefix = "${var.source_network}"
+ destination_address_prefix = "*"
+ }
+}
+
+# A network interface.
+
+resource "azurerm_network_interface" "vyos-nic" {
+ name = "${var.prefix}vyos-nic"
+ location = "${var.location}"
+ resource_group_name = "${var.resource_group}"
+
+ ip_configuration {
+ name = "${var.prefix}ipconfig"
+ subnet_id = "${azurerm_subnet.subnet.id}"
+ private_ip_address_allocation = "Dynamic"
+ public_ip_address_id = "${azurerm_public_ip.vyos-pip.id}"
+ }
+}
+
+# Add a public IP address.
+
+resource "azurerm_public_ip" "vyos-pip" {
+ name = "${var.prefix}-ip"
+ location = "${var.location}"
+ resource_group_name = "${var.resource_group}"
+ allocation_method = "Dynamic"
+}
+
+# Build a virtual machine. This is a standard VyOS instance from
+# Marketplace.
+
+resource "azurerm_virtual_machine" "vyos" {
+ name = "${var.hostname}-vyos"
+ location = "${var.location}"
+ resource_group_name = "${var.resource_group}"
+ vm_size = "${var.vm_size}"
+
+ network_interface_ids = ["${azurerm_network_interface.vyos-nic.id}"]
+ delete_os_disk_on_termination = "true"
+
+# To find information about the plan, use the command:
+# az vm image list --offer vyos --all
+
+ plan {
+ publisher = "sentriumsl"
+ name = "vyos-1-3"
+ product = "vyos-1-2-lts-on-azure"
+ }
+
+ storage_image_reference {
+ publisher = "${var.image_publisher}"
+ offer = "${var.image_offer}"
+ sku = "${var.image_sku}"
+ version = "${var.image_version}"
+ }
+
+ storage_os_disk {
+ name = "${var.hostname}-osdisk"
+ managed_disk_type = "Standard_LRS"
+ caching = "ReadWrite"
+ create_option = "FromImage"
+ }
+
+ os_profile {
+ computer_name = "${var.hostname}"
+ admin_username = "${var.admin_username}"
+ admin_password = "${var.admin_password}"
+ }
+
+ os_profile_linux_config {
+ disable_password_authentication = false
+ }
+}
+
+data "azurerm_public_ip" "example" {
+ depends_on = ["azurerm_virtual_machine.vyos"]
+ name = "vyos-ip"
+ resource_group_name = "${var.resource_group}"
+}
+output "public_ip_address" {
+ value = data.azurerm_public_ip.example.ip_address
+}
+
+# IP of AZ instance copied to a file ip.txt in the local system.
+
+resource "local_file" "ip" {
+ content = data.azurerm_public_ip.example.ip_address
+ filename = "ip.txt"
+}
+
+# Connect to the Ansible control node via SSH
+
+resource "null_resource" "nullremote1" {
+depends_on = ["azurerm_virtual_machine.vyos"]
+connection {
+ type = "ssh"
+ user = "root"
+ password = var.password
+ host = var.host
+}
+
+# Copy the ip.txt file to the Ansible control node from the local
+# system
+
+ provisioner "file" {
+ source = "ip.txt"
+ destination = "/root/az/ip.txt"
+ }
+}
+
+resource "null_resource" "nullremote2" {
+depends_on = ["azurerm_virtual_machine.vyos"]
+connection {
+ type = "ssh"
+ user = "root"
+ password = var.password
+ host = var.host
+}
+
+# Run the Ansible playbook on the remote Linux OS
+
+provisioner "remote-exec" {
+
+ inline = [
+ "cd /root/az/",
+ "ansible-playbook instance.yml"
+]
+}
+}
+```
+
+`var.tf`
+
+```none
+##############################################################################
+# Variables File
+#
+# Default values for all variables used in Terraform code.
+##############################################################################
+
+variable "resource_group" {
+ description = "The name of your Azure Resource Group."
+ default = "my_resource_group"
+}
+
+variable "prefix" {
+ description = "This prefix will be included in the name of some resources."
+ default = "vyos"
+}
+
+variable "hostname" {
+ description = "Virtual machine hostname. Used for local hostname, DNS, and storage-related names."
+ default = "vyos_terraform"
+}
+
+variable "location" {
+ description = "The region where the virtual network is created."
+ default = "centralus"
+}
+
+variable "virtual_network_name" {
+ description = "The name for your virtual network."
+ default = "vnet"
+}
+
+variable "address_space" {
+ description = "The address space that is used by the virtual network. You can supply more than one address space. Changing this forces a new resource to be created."
+ default = "10.0.0.0/16"
+}
+
+variable "subnet_prefix" {
+ description = "The address prefix to use for the subnet."
+ default = "10.0.10.0/24"
+}
+
+variable "storage_account_tier" {
+ description = "Defines the storage tier. Valid options are Standard and Premium."
+ default = "Standard"
+}
+
+variable "storage_replication_type" {
+ description = "Defines the replication type to use for this storage account. Valid options include LRS, GRS etc."
+ default = "LRS"
+}
+
+# The most cost-effective size
+
+variable "vm_size" {
+ description = "Specifies the size of the virtual machine."
+ default = "Standard_B1s"
+}
+
+variable "image_publisher" {
+ description = "Name of the publisher of the image (az vm image list)"
+ default = "sentriumsl"
+}
+
+variable "image_offer" {
+ description = "Name of the offer (az vm image list)"
+ default = "vyos-1-2-lts-on-azure"
+}
+
+variable "image_sku" {
+ description = "Image SKU to apply (az vm image list)"
+ default = "vyos-1-3"
+}
+
+variable "image_version" {
+ description = "Version of the image to apply (az vm image list)"
+ default = "1.3.3"
+}
+
+variable "admin_username" {
+ description = "Administrator user name"
+ default = "vyos"
+}
+
+variable "admin_password" {
+ description = "Administrator password"
+ type = string
+ sensitive = true
+}
+
+variable "source_network" {
+ description = "Allow access from this network prefix. Defaults to '*'."
+ default = "*"
+}
+
+variable "password" {
+ description = "pass for Ansible"
+ type = string
+ sensitive = true
+}
+variable "host"{
+ description = "IP of my Ansible"
+}
+```
+
+`terraform.tfvars`
+
+```none
+password = "" # password for Ansible SSH
+host = "" # IP of my Ansible
+```
+
+
+## Structure of files in Ansible for Azure
+
+```none
+.
+├── group_vars
+ └── all
+├── ansible.cfg
+└── instance.yml
+```
+
+
+## File contents of Ansible for Azure
+
+`ansible.cfg`
+
+```none
+[defaults]
+inventory = /root/az/ip.txt
+host_key_checking= False
+remote_user=vyos
+```
+
+`instance.yml`
+
+```none
+##############################################################################
+# About tasks:
+# "Wait 300 seconds, but only start checking after 60 seconds" - Tries
+# to make SSH connection every 60 seconds until 300 seconds.
+# "Configure general settings for the VyOS hosts group" - Provision
+# the Azure VyOS node.
+# Add all necessary commands for VyOS under the block "lines:"
+##############################################################################
+
+
+- name: integration of terraform and ansible
+ hosts: all
+ gather_facts: 'no'
+
+ tasks:
+
+ - name: "Wait 300 seconds, but only start checking after 60 seconds"
+ wait_for_connection:
+ delay: 60
+ timeout: 300
+
+ - name: "Configure general settings for the VyOS hosts group"
+ vyos_config:
+ lines:
+ - set system name-server xxx.xxx.xxx.xxx
+ save:
+ true
+```
+
+`group_vars/all`
+
+```none
+ansible_connection: ansible.netcommon.network_cli
+ansible_network_os: vyos.vyos.vyos
+
+# user and password gets from terraform variables "admin_username" and "admin_password" in the file /root/azvyos/var.tf
+ansible_user: vyos
+ansible_ssh_pass: "{{ vault_vyos_ssh_pass }}"
+```
+
+
+## Source files on GitHub
+
+All files related to deploying VyOS on Azure with Terraform and Ansible
+can be found in the [vyos-automation] repository.
+
+[vyos-automation]: <https://github.com/vyos/vyos-automation/tree/main/TerraformCloud/Azure_terraform_ansible_single_vyos_instance-main>
diff --git a/docs/automation/terraform/md-terraformGoogle.md b/docs/automation/terraform/md-terraformGoogle.md
new file mode 100644
index 00000000..f9c002a7
--- /dev/null
+++ b/docs/automation/terraform/md-terraformGoogle.md
@@ -0,0 +1,703 @@
+---
+lastproofread: '2026-03-23'
+---
+
+(terraformgoogle)=
+
+# Deploy VyOS on Google Cloud with Terraform and Ansible
+
+Using Terraform, you can quickly deploy VyOS-based infrastructure on
+Google Cloud Platform (GCP) and remove the
+infrastructure when it's no longer needed.
+Additionally, you can use Ansible for provisioning.
+
+On this page you'll learn how to:
+
+- Create the necessary files for Terraform and Ansible.
+- Use Terraform to create a single instance on GCP and use Ansible for
+ provisioning.
+
+## Prepare to deploy VyOS with Terraform on GCP
+
+To create a single instance and install your configuration using
+Terraform, Ansible, and GCP, follow these steps:
+
+### GCP
+
+1. Create an account with GCP and a new project.
+
+```{image} /_static/images/project.webp
+:align: center
+:alt: Network Topology Diagram
+:width: 50%
+```
+
+2. Create a service account and download your key (a JSON file).
+
+```{image} /_static/images/service.webp
+:align: center
+:alt: Network Topology Diagram
+:width: 50%
+```
+
+```{image} /_static/images/key.webp
+:align: center
+:alt: Network Topology Diagram
+:width: 50%
+```
+
+The .JSON file downloads automatically after you create it and looks
+like the following:
+
+```{image} /_static/images/json.webp
+:align: center
+:alt: Network Topology Diagram
+:width: 50%
+```
+
+### Terraform
+
+1. Create an UNIX or Windows instance.
+
+2. Download and install
+ [Terraform](https://developer.hashicorp.com/terraform/install).
+
+3. Create the folder. For example, `/root/google`.
+
+```none
+mkdir /root/google
+```
+
+4. Copy all files into your Terraform project `/root/google`
+ (`vyos.tf`, `var.tf`, `terraform.tfvars`, `mykey.json`).
+ For more details,
+ see [Structure of files Terraform for Google Cloud](#structure-of-files-in-terraform-for-google-cloud)
+
+<!-- -->
+
+5. Run the following commands:
+
+```none
+cd /<your folder>
+terraform init
+```
+
+### Ansible
+
+1. Create an UNIX instance either locally or in the cloud.
+
+2. Download and install Ansible
+
+3. Create the folder for example /root/google/
+
+4. Copy all files into your Ansible project `/root/google/`
+ (`ansible.cfg`, `instance.yml`, `mykey.json`, and `all`). For more
+ details, see [Structure of files in Ansible for Google Cloud](#structure-of-files-in-ansible-for-google-cloud)
+
+You obtain `mykey.json` when you create a service account in GCP
+and download the key (a JSON file).
+
+### Deploy with Terraform
+
+Run the following commands on your Terraform instance:
+
+```none
+cd /<your folder>
+terraform plan
+terraform apply
+yes
+```
+
+## Create a GCP instance and check its configuration
+
+```none
+# terraform apply
+
+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:
+
+ # google_compute_firewall.tcp_22[0] will be created
+ + resource "google_compute_firewall" "tcp_22" {
+ + creation_timestamp = (known after apply)
+ + destination_ranges = (known after apply)
+ + direction = (known after apply)
+ + enable_logging = (known after apply)
+ + id = (known after apply)
+ + name = "vyos-tcp-22"
+ + network = "default"
+ + priority = 1000
+ + project = "vyosproject"
+ + self_link = (known after apply)
+ + source_ranges = [
+ + "0.0.0.0/0",
+ ]
+ + target_tags = [
+ + "vyos-deployment",
+ ]
+
+ + allow {
+ + ports = [
+ + "22",
+ ]
+ + protocol = "tcp"
+ }
+ }
+
+ # google_compute_firewall.udp_500_4500[0] will be created
+ + resource "google_compute_firewall" "udp_500_4500" {
+ + creation_timestamp = (known after apply)
+ + destination_ranges = (known after apply)
+ + direction = (known after apply)
+ + enable_logging = (known after apply)
+ + id = (known after apply)
+ + name = "vyos-udp-500-4500"
+ + network = "default"
+ + priority = 1000
+ + project = "vyosproject"
+ + self_link = (known after apply)
+ + source_ranges = [
+ + "0.0.0.0/0",
+ ]
+ + target_tags = [
+ + "vyos-deployment",
+ ]
+
+ + allow {
+ + ports = [
+ + "500",
+ + "4500",
+ ]
+ + protocol = "udp"
+ }
+ }
+
+ # google_compute_instance.default will be created
+ + resource "google_compute_instance" "default" {
+ + can_ip_forward = true
+ + cpu_platform = (known after apply)
+ + current_status = (known after apply)
+ + deletion_protection = false
+ + effective_labels = (known after apply)
+ + guest_accelerator = (known after apply)
+ + id = (known after apply)
+ + instance_id = (known after apply)
+ + label_fingerprint = (known after apply)
+ + machine_type = "n2-highcpu-4"
+ + metadata = {
+ + "enable-oslogin" = "FALSE"
+ + "serial-port-enable" = "TRUE"
+ + "user-data" = ""
+ }
+ + metadata_fingerprint = (known after apply)
+ + min_cpu_platform = (known after apply)
+ + name = "vyos"
+ + project = "vyosproject"
+ + self_link = (known after apply)
+ + tags_fingerprint = (known after apply)
+ + terraform_labels = (known after apply)
+ + zone = "us-west1-a"
+
+ + boot_disk {
+ + auto_delete = true
+ + device_name = (known after apply)
+ + disk_encryption_key_sha256 = (known after apply)
+ + kms_key_self_link = (known after apply)
+ + mode = "READ_WRITE"
+ + source = (known after apply)
+
+ + initialize_params {
+ + image = "projects/sentrium-public/global/images/vyos-1-3-5-20231222143039"
+ + labels = (known after apply)
+ + provisioned_iops = (known after apply)
+ + provisioned_throughput = (known after apply)
+ + size = (known after apply)
+ + type = (known after apply)
+ }
+ }
+
+ + network_interface {
+ + internal_ipv6_prefix_length = (known after apply)
+ + ipv6_access_type = (known after apply)
+ + ipv6_address = (known after apply)
+ + name = (known after apply)
+ + network = "default"
+ + network_ip = (known after apply)
+ + nic_type = "GVNIC"
+ + stack_type = (known after apply)
+ + subnetwork = "default"
+ + subnetwork_project = (known after apply)
+
+ + access_config {
+ + nat_ip = (known after apply)
+ + network_tier = (known after apply)
+ }
+ }
+ }
+
+ # local_file.ip will be created
+ + resource "local_file" "ip" {
+ + content = (known after apply)
+ + content_base64sha256 = (known after apply)
+ + content_base64sha512 = (known after apply)
+ + content_md5 = (known after apply)
+ + content_sha1 = (known after apply)
+ + content_sha256 = (known after apply)
+ + content_sha512 = (known after apply)
+ + directory_permission = "0777"
+ + file_permission = "0777"
+ + filename = "ip.txt"
+ + id = (known after apply)
+ }
+
+ # null_resource.SSHconnection1 will be created
+ + resource "null_resource" "SSHconnection1" {
+ + id = (known after apply)
+ }
+
+ # null_resource.SSHconnection2 will be created
+ + resource "null_resource" "SSHconnection2" {
+ + id = (known after apply)
+ }
+
+Plan: 6 to add, 0 to change, 0 to destroy.
+
+Changes to Outputs:
+ + public_ip_address = (known after apply)
+╷
+│ Warning: Quoted references are deprecated
+│
+│ on vyos.tf line 126, in resource "null_resource" "SSHconnection1":
+│ 126: depends_on = ["google_compute_instance.default"]
+│
+│ In this context, references are expected literally rather than in quotes. Terraform 0.11 and earlier required quotes, but quoted references are now deprecated and will be removed in a
+│ future version of Terraform. Remove the quotes surrounding this reference to silence this warning.
+│
+│ (and one more similar warning elsewhere)
+╵
+
+Do you want to perform these actions?
+ Terraform will perform the actions described above.
+ Only 'yes' will be accepted to approve.
+
+ Enter a value: yes
+
+google_compute_firewall.udp_500_4500[0]: Creating...
+google_compute_firewall.tcp_22[0]: Creating...
+google_compute_instance.default: Creating...
+google_compute_firewall.udp_500_4500[0]: Still creating... [10s elapsed]
+google_compute_firewall.tcp_22[0]: Still creating... [10s elapsed]
+google_compute_instance.default: Still creating... [10s elapsed]
+google_compute_firewall.tcp_22[0]: Creation complete after 16s [id=projects/vyosproject/global/firewalls/vyos-tcp-22]
+google_compute_firewall.udp_500_4500[0]: Creation complete after 16s [id=projects/vyosproject/global/firewalls/vyos-udp-500-4500]
+google_compute_instance.default: Creation complete after 20s [id=projects/vyosproject/zones/us-west1-a/instances/vyos]
+null_resource.SSHconnection1: Creating...
+null_resource.SSHconnection2: Creating...
+null_resource.SSHconnection1: Provisioning with 'file'...
+null_resource.SSHconnection2: Provisioning with 'remote-exec'...
+null_resource.SSHconnection2 (remote-exec): Connecting to remote host via SSH...
+null_resource.SSHconnection2 (remote-exec): Host: 10.***.***.104
+null_resource.SSHconnection2 (remote-exec): User: root
+null_resource.SSHconnection2 (remote-exec): Password: true
+null_resource.SSHconnection2 (remote-exec): Private key: false
+null_resource.SSHconnection2 (remote-exec): Certificate: false
+null_resource.SSHconnection2 (remote-exec): SSH Agent: false
+null_resource.SSHconnection2 (remote-exec): Checking Host Key: false
+null_resource.SSHconnection2 (remote-exec): Target Platform: unix
+local_file.ip: Creating...
+local_file.ip: Creation complete after 0s [id=7d568c3b994a018c942a3cdb952ccbf3c729d0ca]
+null_resource.SSHconnection2 (remote-exec): Connected!
+null_resource.SSHconnection1: Creation complete after 4s [id=5175298735911137161]
+
+null_resource.SSHconnection2 (remote-exec): PLAY [integration of terraform and ansible] ************************************
+
+null_resource.SSHconnection2 (remote-exec): TASK [Wait 300 seconds, but only start checking after 60 seconds] **************
+null_resource.SSHconnection2: Still creating... [10s elapsed]
+null_resource.SSHconnection2: Still creating... [20s elapsed]
+null_resource.SSHconnection2: Still creating... [30s elapsed]
+null_resource.SSHconnection2: Still creating... [40s elapsed]
+null_resource.SSHconnection2: Still creating... [50s elapsed]
+null_resource.SSHconnection2: Still creating... [1m0s elapsed]
+null_resource.SSHconnection2: Still creating... [1m10s elapsed]
+null_resource.SSHconnection2 (remote-exec): ok: [104.***.***.158]
+
+null_resource.SSHconnection2 (remote-exec): TASK [Configure general settings for the vyos hosts group] *********************
+null_resource.SSHconnection2: Still creating... [1m20s elapsed]
+null_resource.SSHconnection2 (remote-exec): changed: [104.***.***.158]
+
+null_resource.SSHconnection2 (remote-exec): PLAY RECAP *********************************************************************
+null_resource.SSHconnection2 (remote-exec): 104.***.***.158 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
+
+null_resource.SSHconnection2: Creation complete after 1m22s [id=3355727070503709742]
+
+Apply complete! Resources: 6 added, 0 changed, 0 destroyed.
+
+Outputs:
+
+public_ip_address = "104.***.***.158"
+```
+
+After running all the commands, your VyOS instance is deployed on
+GCP with your specified configuration.
+To delete the instance, type the following command:
+
+```none
+terraform destroy
+```
+
+## Troubleshooting
+
+- Increase the timeout value in `instance.yml` from 300 seconds to
+ 500 seconds or more (depends on your location). Ensure that the
+ security group allows access to the instance.
+- If Terraform doesn't connect via SSH to your Ansible instance:
+ Check the correct login and password in the `VyOS.tf` file.
+
+```none
+connection {
+ type = "ssh"
+ user = "root" # open root access using login and password on your Ansible
+ password = var.password # check password in the file terraform.tfvars isn't empty
+ host = var.host # check the correct IP address of your Ansible host
+}
+```
+
+Verify that Ansible can ping from Terraform.
+
+## Structure of files in Terraform for Google Cloud
+
+```none
+.
+├── vyos.tf # The main script
+├── ***.JSON # The credential file from GCP
+├── var.tf # The file of all variables in "vyos.tf"
+└── terraform.tfvars # The value of all variables (passwords, login, IP addresses and so on)
+```
+
+## File contents of Terraform for Google Cloud
+
+`vyos.tf`
+
+```none
+##############################################################################
+# Build a VyOS VM from the Marketplace
+#
+# After deploying the GCP instance and getting an IP address, the IP address is copied into the file
+#"ip.txt" and copied to the Ansible node for provisioning.
+##############################################################################
+
+terraform {
+ required_providers {
+ google = {
+ source = "hashicorp/google"
+ }
+ }
+}
+
+provider "google" {
+ project = var.project_id
+ request_timeout = "60s"
+ credentials = file(var.gcp_auth_file)
+}
+
+locals {
+ network_interfaces = [for i, n in var.networks : {
+ network = n,
+ subnetwork = length(var.sub_networks) > i ? element(var.sub_networks, i) : null
+ external_ip = length(var.external_ips) > i ? element(var.external_ips, i) : "NONE"
+ }
+ ]
+}
+
+resource "google_compute_instance" "default" {
+ name = var.goog_cm_deployment_name
+ machine_type = var.machine_type
+ zone = var.zone
+
+ metadata = {
+ enable-oslogin = "FALSE"
+ serial-port-enable = "TRUE"
+ user-data = var.vyos_user_data
+ }
+ boot_disk {
+ initialize_params {
+ image = var.image
+ }
+ }
+
+ can_ip_forward = true
+
+ dynamic "network_interface" {
+ for_each = local.network_interfaces
+ content {
+ network = network_interface.value.network
+ subnetwork = network_interface.value.subnetwork
+ nic_type = "GVNIC"
+ dynamic "access_config" {
+ for_each = network_interface.value.external_ip == "NONE" ? [] : [1]
+ content {
+ nat_ip = network_interface.value.external_ip == "EPHEMERAL" ? null : network_interface.value.external_ip
+ }
+ }
+ }
+ }
+}
+
+resource "google_compute_firewall" "tcp_22" {
+ count = var.enable_tcp_22 ? 1 : 0
+
+ name = "${var.goog_cm_deployment_name}-tcp-22"
+ network = element(var.networks, 0)
+
+ allow {
+ ports = ["22"]
+ protocol = "tcp"
+ }
+
+ source_ranges = ["0.0.0.0/0"]
+
+ target_tags = ["${var.goog_cm_deployment_name}-deployment"]
+}
+
+resource "google_compute_firewall" "udp_500_4500" {
+ count = var.enable_udp_500_4500 ? 1 : 0
+
+ name = "${var.goog_cm_deployment_name}-udp-500-4500"
+ network = element(var.networks, 0)
+
+ allow {
+ ports = ["500", "4500"]
+ protocol = "udp"
+ }
+
+ source_ranges = ["0.0.0.0/0"]
+
+ target_tags = ["${var.goog_cm_deployment_name}-deployment"]
+}
+
+output "public_ip_address" {
+ value = google_compute_instance.default.network_interface[0].access_config[0].nat_ip
+}
+
+##############################################################################
+#
+# IP of google instance copied to a file ip.txt in local system Terraform
+# ip.txt looks like:
+# cat ./ip.txt
+# ххх.ххх.ххх.ххх
+##############################################################################
+
+resource "local_file" "ip" {
+ content = google_compute_instance.default.network_interface[0].access_config[0].nat_ip
+ filename = "ip.txt"
+}
+
+#connecting to the Ansible control node using SSH connection
+
+##############################################################################
+# Steps "SSHconnection1" and "SSHconnection2" need to get file ip.txt from the terraform node and start remotely the playbook of Ansible.
+##############################################################################
+
+resource "null_resource" "SSHconnection1" {
+depends_on = ["google_compute_instance.default"]
+connection {
+ type = "ssh"
+ user = "root"
+ password = var.password
+ host = var.host
+}
+
+#copying the ip.txt file to the Ansible control node from local system
+
+ provisioner "file" {
+ source = "ip.txt"
+ destination = "/root/google/ip.txt" # The folder of your Ansible project
+ }
+}
+
+resource "null_resource" "SSHconnection2" {
+depends_on = ["google_compute_instance.default"]
+connection {
+ type = "ssh"
+ user = "root"
+ password = var.password
+ host = var.host
+}
+
+#command to run Ansible playbook on remote Linux OS
+
+provisioner "remote-exec" {
+ inline = [
+ "cd /root/google/",
+ "ansible-playbook instance.yml" # more detailed in "File contents of Ansible for Google Cloud"
+]
+}
+}
+```
+
+`var.tf`
+
+```none
+variable "image" {
+ type = string
+ default = "projects/sentrium-public/global/images/vyos-1-3-5-20231222143039"
+}
+
+variable "project_id" {
+ type = string
+}
+
+variable "zone" {
+ type = string
+}
+
+##############################################################################
+# You can choose a lower cost machine type than n2-highcpu-4
+##############################################################################
+
+variable "machine_type" {
+ type = string
+ default = "n2-highcpu-4"
+}
+
+variable "networks" {
+ description = "The network name to attach the VM instance."
+ type = list(string)
+ default = ["default"]
+}
+
+variable "sub_networks" {
+ description = "The sub network name to attach the VM instance."
+ type = list(string)
+ default = ["default"]
+}
+
+variable "external_ips" {
+ description = "The external IPs assigned to the VM for public access."
+ type = list(string)
+ default = ["EPHEMERAL"]
+}
+
+variable "enable_tcp_22" {
+ description = "Allow SSH traffic from the Internet"
+ type = bool
+ default = true
+}
+
+variable "enable_udp_500_4500" {
+ description = "Allow IKE/IPSec traffic from the Internet"
+ type = bool
+ default = true
+}
+
+variable "vyos_user_data" {
+ type = string
+ default = ""
+}
+
+// Marketplace requires this variable name to be declared
+variable "goog_cm_deployment_name" {
+ description = "VyOS Universal Router Deployment"
+ type = string
+ default = "vyos"
+}
+
+# GCP authentication file
+variable "gcp_auth_file" {
+ type = string
+ description = "GCP authentication file"
+}
+
+variable "password" {
+ description = "pass for Ansible"
+ type = string
+ sensitive = true
+}
+variable "host"{
+ description = "The IP of my Ansible"
+ type = string
+}
+```
+
+`terraform.tfvars`
+
+```none
+##############################################################################
+# Must be filled in
+##############################################################################
+
+zone = "us-west1-a"
+gcp_auth_file = "/root/***/***.json" # path of your .json file
+project_id = "" # the google project
+password = "" # password for Ansible SSH
+host = "" # IP of my Ansible
+```
+
+## Structure of files in Ansible for Google Cloud
+
+```none
+.
+├── group_vars
+ └── all
+├── ansible.cfg
+└── instance.yml
+```
+
+## File contents of Ansible for Google Cloud
+
+`ansible.cfg`
+
+```none
+[defaults]
+inventory = /root/google/ip.txt
+host_key_checking= False
+remote_user=vyos
+```
+
+`instance.yml`
+
+```none
+##############################################################################
+# About tasks:
+# "Wait 300 seconds, but only start checking after 60 seconds" - try to make ssh connection every 60 seconds until 300 seconds
+# "Configure general settings for the VyOS hosts group" - make provisioning into Google Cloud VyOS node
+# Add all necessary VyOS commands under the "lines:" block
+##############################################################################
+
+
+- name: integration of terraform and ansible
+ hosts: all
+ gather_facts: 'no'
+
+ tasks:
+
+ - name: "Wait 300 seconds, but only start checking after 60 seconds"
+ wait_for_connection:
+ delay: 60
+ timeout: 300
+
+ - name: "Configure general settings for the VyOS hosts group"
+ vyos_config:
+ lines:
+ - set system name-server xxx.xxx.xxx.xxx
+ save:
+ true
+```
+
+`group_vars/all`
+
+```none
+ansible_connection: ansible.netcommon.network_cli
+ansible_network_os: vyos.vyos.vyos
+ansible_user: vyos
+ansible_ssh_pass: vyos
+```
+
+
+## Source files on GitHub
+
+All files related to deploying VyOS on Google Cloud Platform with
+Terraform and Ansible can be found in the [vyos-automation] repository.
+
+[vyos-automation]: <https://github.com/vyos/vyos-automation/tree/main/TerraformCloud/Google_terraform_ansible_single_vyos_instance-main>
diff --git a/docs/automation/terraform/md-terraformvSphere.md b/docs/automation/terraform/md-terraformvSphere.md
new file mode 100644
index 00000000..abcef5fa
--- /dev/null
+++ b/docs/automation/terraform/md-terraformvSphere.md
@@ -0,0 +1,388 @@
+---
+lastproofread: '2026-03-23'
+---
+
+(terraformvSphere)=
+
+# Deploy VyOS on VMware vSphere with Terraform and Ansible
+
+You can use Terraform to quickly deploy VyOS-based infrastructure
+on VMware vSphere (hereafter referred to as *vSphere*) and remove
+infrastructure when it's no longer needed.
+Additionally, you can use Ansible for provisioning.
+
+On this page you'll learn how to:
+
+- Create the necessary files for Terraform and Ansible.
+- Use Terraform to create a single instance on vSphere and use Ansible for
+ provisioning.
+
+## Prepare to deploy VyOS with Terraform on vSphere
+
+To create a single instance and install your configuration using
+Terraform, Ansible, and vSphere, follow these steps:
+
+### vSphere
+
+- Add all necessary data to the `terraform.tfvars`
+ [file](<https://github.com/vyos/vyos-automation/blob/main/TerraformCloud/Vsphere_terraform_ansible_single_vyos_instance-main/terraform.tfvars>)
+ and create resources.
+
+### Terraform
+
+- Create an UNIX or Windows instance.
+- Download and install
+ [Terraform](https://developer.hashicorp.com/terraform/install).
+- Create the folder for example `/root/vsphereterraform`.
+
+```none
+mkdir /root/vsphereterraform
+```
+
+- Copy all files into your Terraform project `/root/vsphereterraform`
+ (`vyos.tf`, `var.tf`, `terraform.tfvars`, `version.tf`).
+ For more details,
+ see [Structure of files in Terraform for vSphere](#structure-of-files-in-terraform-for-vsphere)
+- Run the following commands:
+
+```none
+cd /<your folder>
+terraform init
+```
+
+
+### Ansible
+
+- Create an UNIX instance either locally or in the cloud.
+- Download and install Ansible.
+- Create the folder. For example, `/root/vsphereterraform/`.
+- Copy all files into your Ansible project `/root/vsphereterraform/`
+ (`ansible.cfg`, `instance.yml`, `all`). For more details, see
+ [Structure of files in Ansible for vSphere](#structure-of-files-in-ansible-for-vsphere)
+
+### Deploy with Terraform
+
+Run the following commands on your Terraform instance:
+
+```none
+cd /<your folder>
+terraform plan
+terraform apply
+yes
+```
+
+After executing these commands, your VyOS instance is deployed to
+vSphere with your configuration.
+If you need to delete the instance, run the following command:
+
+```none
+terraform destroy
+```
+
+## Structure of files in Terraform for vSphere
+
+```none
+.
+├── vyos.tf # The main script.
+├── versions.tf # File for Terraform version.
+├── var.tf # File for Terraform version.
+└── terraform.tfvars # Values for all variables (passwords,
+ # login, IP addresses, etc.).
+```
+
+## File contents of Terraform for vSphere
+
+`vyos.tf`
+
+```none
+provider "vsphere" {
+ user = var.vsphere_user
+ password = var.vsphere_password
+ vsphere_server = var.vsphere_server
+ allow_unverified_ssl = true
+}
+
+data "vsphere_datacenter" "datacenter" {
+ name = var.datacenter
+}
+
+data "vsphere_datastore" "datastore" {
+ name = var.datastore
+ datacenter_id = data.vsphere_datacenter.datacenter.id
+}
+
+data "vsphere_compute_cluster" "cluster" {
+ name = var.cluster
+ datacenter_id = data.vsphere_datacenter.datacenter.id
+}
+
+data "vsphere_resource_pool" "default" {
+ name = format("%s%s", data.vsphere_compute_cluster.cluster.name, "/Resources/terraform") # set as you need
+ datacenter_id = data.vsphere_datacenter.datacenter.id
+}
+
+data "vsphere_host" "host" {
+ name = var.host
+ datacenter_id = data.vsphere_datacenter.datacenter.id
+}
+
+data "vsphere_network" "network" {
+ name = var.network_name
+ datacenter_id = data.vsphere_datacenter.datacenter.id
+}
+
+# Deployment of VM from Remote OVF
+resource "vsphere_virtual_machine" "vmFromRemoteOvf" {
+ name = var.remotename
+ datacenter_id = data.vsphere_datacenter.datacenter.id
+ datastore_id = data.vsphere_datastore.datastore.id
+ host_system_id = data.vsphere_host.host.id
+ resource_pool_id = data.vsphere_resource_pool.default.id
+ network_interface {
+ network_id = data.vsphere_network.network.id
+ }
+ wait_for_guest_net_timeout = 2
+ wait_for_guest_ip_timeout = 2
+
+ ovf_deploy {
+ allow_unverified_ssl_cert = true
+ remote_ovf_url = var.url_ova
+ disk_provisioning = "thin"
+ ip_protocol = "IPv4"
+ ip_allocation_policy = "dhcpPolicy"
+ ovf_network_map = {
+ "Network 1" = data.vsphere_network.network.id
+ "Network 2" = data.vsphere_network.network.id
+ }
+ }
+ vapp {
+ properties = {
+ "password" = "12345678",
+ "local-hostname" = "terraform_vyos"
+ }
+ }
+}
+
+output "ip" {
+ description = "default ip address of the deployed VM"
+ value = vsphere_virtual_machine.vmFromRemoteOvf.default_ip_address
+}
+
+# IP of vSphere instance copied to a file ip.txt in local system
+
+resource "local_file" "ip" {
+ content = vsphere_virtual_machine.vmFromRemoteOvf.default_ip_address
+ filename = "ip.txt"
+}
+
+#Connecting to the Ansible control node using SSH connection
+
+resource "null_resource" "nullremote1" {
+depends_on = ["vsphere_virtual_machine.vmFromRemoteOvf"]
+connection {
+ type = "ssh"
+ user = "root"
+ password = var.ansiblepassword
+ host = var.ansiblehost
+
+}
+
+# Copying the ip.txt file to the Ansible control node from local system
+
+ provisioner "file" {
+ source = "ip.txt"
+ destination = "/root/vsphere/ip.txt"
+ }
+}
+
+resource "null_resource" "nullremote2" {
+depends_on = ["vsphere_virtual_machine.vmFromRemoteOvf"]
+connection {
+ type = "ssh"
+ user = "root"
+ password = var.ansiblepassword
+ host = var.ansiblehost
+}
+
+# Command to run ansible playbook on remote Linux OS
+
+provisioner "remote-exec" {
+
+ inline = [
+ "cd /root/vsphere/",
+ "ansible-playbook instance.yml"
+]
+}
+}
+```
+
+`versions.tf`
+
+```none
+# Copyright (c) HashiCorp, Inc.
+# SPDX-License-Identifier: MPL-2.0
+
+terraform {
+ required_providers {
+ vsphere = {
+ source = "hashicorp/vsphere"
+ version = "2.4.0"
+ }
+ }
+}
+```
+
+`var.tf`
+
+```none
+# Copyright (c) HashiCorp, Inc.
+# SPDX-License-Identifier: MPL-2.0
+
+variable "vsphere_server" {
+ description = "vSphere server"
+ type = string
+}
+
+variable "vsphere_user" {
+ description = "vSphere username"
+ type = string
+}
+
+variable "vsphere_password" {
+ description = "vSphere password"
+ type = string
+ sensitive = true
+}
+
+variable "datacenter" {
+ description = "vSphere data center"
+ type = string
+}
+
+variable "cluster" {
+ description = "vSphere cluster"
+ type = string
+}
+
+variable "datastore" {
+ description = "vSphere datastore"
+ type = string
+}
+
+variable "network_name" {
+ description = "vSphere network name"
+ type = string
+}
+
+variable "host" {
+ description = "Name of your host"
+ type = string
+}
+
+variable "remotename" {
+ description = "The name of your VM"
+ type = string
+}
+
+variable "url_ova" {
+ description = "The URL to the .OVA file or cloud storage"
+ type = string
+}
+
+variable "ansiblepassword" {
+ description = "Ansible password"
+ type = string
+}
+
+variable "ansiblehost" {
+ description = "Ansible host name or IP"
+ type = string
+}
+```
+
+`terraform.tfvars`
+
+```none
+vsphere_user = ""
+vsphere_password = ""
+vsphere_server = ""
+datacenter = ""
+datastore = ""
+cluster = ""
+network_name = ""
+host = ""
+url_ova = ""
+ansiblepassword = ""
+ansiblehost = ""
+remotename = ""
+```
+
+## Structure of files in Ansible for vSphere
+
+```none
+.
+├── group_vars
+ └── all
+├── ansible.cfg
+└── instance.yml
+```
+
+## File contents of Ansible for vSphere
+
+`ansible.cfg`
+
+```none
+[defaults]
+inventory = /root/vsphere/ip.txt
+host_key_checking= False
+remote_user=vyos
+```
+
+`instance.yml`
+
+```none
+##############################################################################
+# About tasks:
+# "Wait 300 seconds, but only start checking after 60 seconds" - try to make ssh connection every 60 seconds until 300 seconds
+# "Configure general settings for the VyOS hosts group" - make provisioning into vSphere VyOS node
+# You have to add all necessary commands of VyOS under the block "lines:"
+##############################################################################
+
+
+- name: integration of terraform and ansible
+ hosts: all
+ gather_facts: 'no'
+
+ tasks:
+
+ - name: "Wait 300 seconds, but only start checking after 60 seconds"
+ wait_for_connection:
+ delay: 60
+ timeout: 300
+ - name: "Configure general settings for the VyOS hosts group"
+ vyos_config:
+ lines:
+ - set system name-server 192.0.2.1
+ save:
+ true
+```
+
+`group_vars/all`
+
+```none
+ansible_connection: ansible.netcommon.network_cli
+ansible_network_os: vyos.vyos.vyos
+
+# user and password gets from terraform variables "admin_username" and "admin_password"
+ansible_user: vyos
+# get from vyos.tf "vapp"
+ansible_ssh_pass: 12345678
+```
+
+
+## Source files on GitHub
+
+All files related to deploying VyOS on vSphere with Terraform and Ansible
+can be found in the [vyos-automation] repository.
+
+[vyos-automation]: <https://github.com/vyos/vyos-automation/tree/main/TerraformCloud/Vsphere_terraform_ansible_single_vyos_instance-main>
diff --git a/docs/automation/terraform/terraformvyos.md b/docs/automation/terraform/terraformvyos.md
new file mode 100644
index 00000000..bfe1b6d1
--- /dev/null
+++ b/docs/automation/terraform/terraformvyos.md
@@ -0,0 +1,44 @@
+---
+lastproofread: '2024-03-03'
+---
+
+(terraformvyos)=
+
+# Terraform for VyOS
+
+VyOS supports development infrastructure via Terraform and
+provisioning via Ansible. Terraform allows you to automate the
+process of deploying instances on many cloud and virtual
+platforms. In this article, we will look at using Terraform to
+deploy VyOS on platforms - AWS, Azure, and vSphere. For more
+details about Terraform please have a look at [link].
+
+You will need to [install] Terraform before proceeding.
+
+Structure of files in the standard Terraform project:
+
+```none
+.
+├── main.tf # The main script
+├── version.tf # File for the changing version of Terraform.
+├── variables.tf # The file of all variables in "main.tf"
+└── terraform.tfvars # The value of all variables (passwords, login, IP addresses and so on)
+```
+
+General commands that we will use for running Terraform scripts
+
+```none
+cd /<your folder> # go to the Terraform project
+terraform init # install all add-ons and providers (AWS, Azure, and so on)
+terraform plan # show what is changing
+terraform apply # run script
+yes # apply running
+```
+
+% stop_vyoslinter
+
+% start_vyoslinter
+
+[install]: https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli
+[link]: https://developer.hashicorp.com/terraform/intro
+