# Ansible for Network Automation: — From Zero to Your First Playbook

Source: https://ai.techclick.in/blog_ansible_network_automation_basics
Markdown: https://ai.techclick.in/blog_ansible_network_automation_basics.md
Publisher: Techclick Infosec Pvt Ltd

Ansible network automation for L1/L2 engineers and RHCE EX294: agentless/declarative/idempotent, the inventory + cisco.ios collection, network_cli connection, and your first ios_config + ios_facts playbook with --check.

Ansible for Network Automation: — From Zero to Your First Playbook student learning map
                     A visual study map for Ansible for Network Automation: — From Zero to Your First Playbook showing learning path, evidence, traps, and practice sequence.

                     TECHCLICK STUDY MAP
                     Ansible for Network Automation: — From Zero to Your...
                     Ansible · learn the flow, prove with evidence, avoid unsafe shortcuts

   1. Start
   🎯 By the end you will be able to

   2. Understand
   Pick where you want to start

   3. Prove
   ① Why Ansible for networks —...

   4. Practice
   ② The pieces — control node,...

                     How to use this page
                     First build the mental model, then connect the concept to a realistic production decision. Finish by testing yourself.
                     Techclick Infosec Pvt Ltd | ai.techclick.in | Training Contact: WhatsApp +91 92772 29456

             Content-specific feature visual for this lesson: use it as the 60-second map before reading the full detail.

             Most engineers think…

             Most engineers first hear "Ansible for networks" and picture installing some software on every router, then writing a giant script that runs commands top-to-bottom like a bash file.

             Wrong on both counts — and it will cost you in interviews. Ansible is  agentless  (nothing installed on the device — it logs in over SSH/API),  declarative  (you describe the desired  state , not the keystrokes), and  idempotent  (re-running a playbook that already matches makes  zero  changes). For routers and switches it does not even use the normal SSH module — it loads a  network_cli  connection that knows how to drive a Cisco/Arista/Juniper CLI. The mental model is a checklist of desired states, not a script of commands.

## ① Why Ansible for networks — drift, typos and 2 a.m. callbacks

 Meet Rahul, an L1 engineer at  Infosys  looking after 500 branch switches. The standard rollout — say, a new login banner and an NTP server on every box — means he opens PuTTY, SSHes in, pastes four lines, saves, logs out, and repeats. Five hundred times. By switch 347 he fat-fingers an IP, by switch 480 he is copy-pasting yesterday's banner, and a week later no two configs are identical. That slow divergence is called  configuration drift , and it is the quiet disease of every hand-run network.

 There is also no record. When something breaks at 2 a.m., nobody can say  who  changed  what  on which box. Rahul is not lazy or careless — he is human, and humans do not scale to 500 identical tasks. This is exactly the gap  Ansible  was built to fill.

 👉 So far: by-hand config means drift, typos and no audit trail. Next: the three words — agentless, declarative, idempotent — that make Ansible different from a shell script.

 Three properties define Ansible, and you must be able to say each one in an interview.  Agentless : you install  no agent  on the device — Ansible logs in over  SSH  (or an API) the same way you do, runs its work, and disconnects. Nothing to install, patch or break on 500 switches.  Declarative : you describe the  desired state  ("this interface should have this description"), not the keystrokes to get there.  Idempotent : run the same playbook ten times and it makes a change only the  first  time — after that it sees the state already matches and does nothing.

  Figure 1 — 500 devices by hand vs one Ansible playbook

 A two-column comparison for the same fleet of network devices. Left in red is the manual way: an engineer SSHes into each box one by one, copy-pastes commands, makes typos, and the running-configs drift apart over time with no single source of truth. Right in green is the Ansible way: one control node reads an inventory and a playbook of desired state, connects over SSH using the network_cli connection, and pushes the same state to every device; re-running changes nothing because it is idempotent. Red marks the error-prone manual path, green marks the automated path, amber marks the playbook decision point.

 500 devices by hand vs one Ansible playbook
   By hand — SSH into each box
 ✗ log in, paste, log out — 500 times
 ✗ one typo on box 347 → silent drift
 ✗ no record of WHO changed WHAT
 ✗ re-doing a change = redo it all by hand
  sw-01
  sw-02
  …sw-500
 each config slowly drifts from the others
 2 a.m. debugging, no rollback
   Ansible — one playbook, all devices
  control node  inventory + playbook
  playbook = desired  state (declarative)

- SSH over network_cli — no agent on the box sw-01 sw-02 …sw-500 re-run = changed=0 (idempotent, safe) same state everywhere, in version control minutes, repeatable, reviewable error-prone / drift controlled / inspected policy / decision key insight applied / converged Read both columns for the same fleet. Left (red) = SSH into each box, drift and typos, no rollback. Right (green) = one control node, one declarative playbook over SSH, re-runnable with changed=0 and kept in version control. One more thing that trips up beginners: for routers and switches, Ansible does not use its normal SSH/shell module (the one it uses for Linux servers). Network gear has a CLI, not a shell, so Ansible loads a special network_cli connection. You point it at the right OS with ansible_network_os , and it knows how to handle the Cisco prompt, paging, and enable mode. We will set those variables in the next section. ### The three words, one tap each Tap each card — these are the answers to "what is Ansible?" that a hiring manager wants to hear. 🪶 Agentless tap to flip No software on the device — Ansible logs in over SSH/API and leaves. So: nothing extra to install or patch on 500 boxes. 🧾 Declarative tap to flip You declare the end state, not the keystrokes. So: the playbook reads like a checklist, and the tool works out the diff. ♻️ Idempotent tap to flip Re-running a matching playbook changes nothing. So: it is safe to run on a schedule and to prove a box is compliant. 🔌 network_cli tap to flip For routers/switches Ansible uses a CLI connection, not the Linux SSH module. So: it handles the prompt + enable mode for you. Daily-life analogy — the society gate-pass register Hand-config is like every flat owner phoning the guard with their own instructions — nobody agrees, and there is no record. Ansible is the society gate-pass register : one written list of who is allowed in (the desired state), the guard checks each visitor against it, and if a name is already on the list he does nothing new. Re-reading the same register tomorrow changes nothing — that is idempotency. And the guard (Ansible) carries the list to every gate; he does not install a copy at each flat (agentless). Quick check · Q1 of 10 Sneha at TCS says: "We run our compliance playbook every night. Won't that re-apply the banner and NTP config 365 times a year and risk breaking something?" What is the correct reassurance? a) Yes — schedule it weekly instead to reduce the risk b) No — Ansible is idempotent: if the state already matches, the nightly run reports changed=0 and touches nothing c) No — because Ansible installs an agent that caches the result d) Yes — you must add a flag to stop it re-running tasks Correct: b. Idempotency is the whole point: a task that finds the desired state already present makes no change and reports changed=0. So a nightly run is safe and even useful as a drift check. There is no agent (it is agentless), and you do not need a special flag to make tasks idempotent — well-written modules like ios_config are idempotent by design. Pause & Predict Predict: Ansible is "agentless" and just uses SSH. Name ONE thing that gets easier because there is no agent, and ONE new thing you now depend on instead. Type your guess. Reveal answer Answer: Easier: nothing to install, version or patch on the 500 devices — onboarding a new switch is just adding it to the inventory, and a vendor agent CVE can never hit you. New dependency: solid SSH reachability and credentials to every device, because SSH (and, for enable mode, the enable password) is now the single path in. If SSH is blocked or the host key is unknown, Ansible cannot reach the box at all — which is why the first gotcha everyone hits is host-key checking. ## ② The pieces — control node, inventory, modules, collections, plays Ansible has a small number of moving parts. Learn the names once and the rest of the series clicks. The control node is the only box with Ansible on it — your laptop or a jump host. Everything else is a managed device with nothing installed. The inventory lists your devices and sorts them into groups — [routers] , [switches] , [mumbai] . Groups matter because you attach shared settings to a group once via group_vars , instead of repeating them per host. A task calls a module ; a group of tasks aimed at a set of hosts is a play ; one or more plays in a YAML file is a playbook . Modules do not all ship in the box anymore. They live in collections you install from Ansible Galaxy . For Cisco IOS you install cisco.ios ; for Arista, arista.eos ; for Juniper, junipernetworks.junos ; for VyOS, vyos.vyos . The Cisco collection depends on the shared ansible.netcommon collection, so installing cisco.ios pulls it in. Figure 2 — Ansible's pieces in one map An architecture map of Ansible for networks. On the left is the control node, the only machine with Ansible installed. It holds the inventory file listing hosts and groups, the playbook of plays and tasks, and collections installed from Ansible Galaxy such as cisco.ios, arista.eos, junipernetworks.junos and vyos.vyos which supply the modules. Connection variables ansible_connection=network_cli, ansible_network_os and become turn each task into a CLI session. The control node fans out over SSH to managed devices grouped as routers and switches; nothing is installed on those devices. Blue marks the trusted control side, amber marks the connection decision, green marks the managed devices that end up in the desired state. The pieces — one control node drives the whole fleet Control node the only box with Ansible installed inventory hosts + [routers]/[switches] groups playbook plays → tasks (YAML, desired state) collections (from Galaxy) cisco.ios · arista.eos · junipernetworks.junos vyos.vyos — they ship the modules connection vars ansible_connection=network_cli ansible_network_os=cisco.ios.ios become: yes (enable) Managed devices nothing installed here — agentless [routers] rtr-mum-01 rtr-pun-01 [switches] sw-blr-01 sw-blr-02 end state = exactly what the playbook declared SSH (network_cli) — no agent, no extra port error-prone / drift controlled / inspected policy / decision key insight applied / converged Look for the one-way arrows: only the control node has Ansible. It reads inventory + collections, then fans plays out over SSH (network_cli) to devices that have nothing installed on them. 👉 So far: control node, inventory + groups, modules from collections, plays + tasks. Next: the connection variables that turn a generic task into a Cisco CLI session — and the YAML rule that breaks everyone's first playbook. Now the network-specific part. By default Ansible would try to SSH and run a shell — useless on a router. So in your inventory or group_vars you set three things. ansible_connection: ansible.netcommon.network_cli loads the CLI connection. ansible_network_os: cisco.ios.ios tells it the platform. And to get into enable mode , you add become: yes with ansible_become_method: enable and an ansible_become_password (the enable secret). inventory.ini + group_vars/routers.yml — where the connection lives # inventory.ini [routers] rtr-mum-01 ansible_host=10.10.1.1 rtr-pun-01 ansible_host=10.10.2.1 # group_vars/routers.yml ansible_connection: ansible.netcommon.network_cli ansible_network_os: cisco.ios.ios ansible_user: netauto ansible_become: yes ansible_become_method: enable ansible_become_password: "&#123;&#123; vault_enable_secret &#125;&#125;" Expected output # group_vars apply to every host in [routers], # so you set the connection ONCE, not per device. # The enable secret is pulled from an Ansible Vault # variable — never hard-coded in the file. Common mistake — "the playbook ran but Ansible just sat there and timed out" Symptom: you wrote a clean playbook, but the run hangs and ends in a timeout or returns garbage. Cause is almost always a wrong or missing ansible_network_os , or forgetting ansible_connection: ansible.netcommon.network_cli — so Ansible tries to open a Linux shell on a device that only has a Cisco CLI. Fix: set both in group_vars and confirm the platform string exactly ( cisco.ios.ios , not ios or cisco_ios ). A second classic: the very first connection prompts to accept the SSH host key and the run stalls — set host_key_checking = False in ansible.cfg for a lab. A word on YAML , because it causes more first-day pain than any module. YAML uses spaces, never tabs , and indentation defines structure — two spaces in is "inside" the line above. A list item starts with - (dash-space). A key: value needs the space after the colon. Mis-indent one line and you get a cryptic "could not find expected ':'" error. When in doubt, run ansible-playbook --syntax-check site.yml before anything else. Quick check · Q2 of 10 Aditya at Wipro writes a playbook for a Cisco router but leaves the connection at the default. The run hangs and times out. Which single change is the fix? a) Add gather_facts: true so it collects facts first b) Install a small agent on the router c) Set ansible_connection: ansible.netcommon.network_cli and ansible_network_os: cisco.ios.ios d) Switch from YAML to JSON for the playbook Correct: c. A Cisco device has a CLI, not a Linux shell, so the default connection times out. Setting network_cli plus the correct ansible_network_os makes Ansible drive the IOS CLI properly. gather_facts does not fix the connection; Ansible is agentless so you never install an agent; and the playbook language (YAML) is unrelated to the connection timeout. Pause & Predict Predict: you put ansible_connection and ansible_network_os in group_vars/routers.yml instead of repeating them on every host line in the inventory. Why is that the better habit? Type your guess. Reveal answer Answer: Because every host in the [routers] group inherits those variables automatically, so you set the connection details exactly once. Add a 50th router to the group and it just works — no copy-paste of connection vars, no risk of one host having a typo'd platform string. group_vars is the single source of truth for "what every router in this group has in common", which is also why secrets like the enable password live there (encrypted with Vault), not scattered across the inventory. ## ③ Your first playbook — gather facts, push one line, run with --check Time to build something real. A first playbook should do two safe things: read state and write one line. We will use ios_facts to gather facts, then ios_config to set an interface description. Both come from the cisco.ios collection. site.yml — your first network playbook --- - name: First IOS playbook hosts: routers gather_facts: false tasks: - name: Collect device facts cisco.ios.ios_facts: - name: Show the IOS version we found ansible.builtin.debug: msg: "Running IOS &#123;&#123; ansible_net_version &#125;&#125; on &#123;&#123; ansible_net_model &#125;&#125;" - name: Set the WAN interface description cisco.ios.ios_config: parents: interface GigabitEthernet0/1 lines: - description WAN-uplink-to-Pune Expected output PLAY [First IOS playbook] ****************************** TASK [Collect device facts] *************************** ok: [rtr-mum-01] TASK [Set the WAN interface description] ************* changed: [rtr-mum-01] PLAY RECAP ******************************************** rtr-mum-01 : ok=3 changed=1 unreachable=0 failed=0 Notice gather_facts: false at the play level — the normal Linux fact-gathering does not work on a router, so we disable it and use the network-specific ios_facts task instead. The facts land in variables like ansible_net_version and ansible_net_model , which you can print or reuse. In ios_config , parents is the section header ( interface GigabitEthernet0/1 ) and lines are the commands inside it. ansible-playbook site.yml --check --diff . (Recreated for clarity — your terminal output matches this.)"> 🖥️ This is the run you will actually watch — a terminal on the control node executing ansible-playbook site.yml --check --diff . (Recreated for clarity — your terminal output matches this.) netauto@control:~/playbooks 1 Command ansible-playbook -i inventory.ini site.yml --check --diff 2 TASK [Collect device facts] ok: [rtr-mum-01] 3 TASK [Set WAN description] changed: [rtr-mum-01] (would change, --check) diff + description WAN-uplink-to-Pune 4 PLAY RECAP ok=3 changed=1 unreachable=0 failed=0 ▶ run 👉 So far: a 3-task playbook that reads facts and sets one description. Next: the run flags — --check, --diff, --limit and -v — that keep you safe and help you debug. You never run a new playbook straight at production. The flags are your seatbelt. --check is a dry run — it reports what would change and touches nothing. --diff shows the precise lines. --limit scopes the run to one box for a first test. And -v (up to -vvvv ) turns up verbosity when something misbehaves. Figure 3 — Desired vs current — why --check is your seatbelt A flow showing how one ios_config task is processed. Step 1 the playbook declares desired state, for example an interface description line. Step 2 ios_facts and the module read the current running-config from the router. Step 3 Ansible compares desired versus current. If they already match, the result is changed equals false and nothing is sent. If they differ, in check mode Ansible only prints the diff and reports what would change; in a real run it pushes only the missing lines and reports changed equals true. Re-running afterwards goes back to changed equals false. Amber marks the compare decision, green marks the safe applied path, lime marks the key insight that check mode is risk-free. Desired vs current — and why --check is your seatbelt 1· desired state playbook: description WAN-uplink 2· read current module reads running-config 3· compare desired == current ? already matches → changed=false (no-op) same differs + --check prints the diff only, device untouched (dry run) differs differs + real run push ONLY the missing lines changed=true run it AGAIN → changed=false proof it is idempotent error-prone / drift controlled / inspected policy / decision key insight applied / converged Follow the compare diamond: if desired already equals current → changed=false (no-op). If they differ, --check only prints the diff (device untouched), a real run pushes ONLY the missing lines → changed=true, and re-running drops back to changed=false. ### ▶ Watch one ios_config task decide what to do You run the playbook against rtr-mum-01. Follow how ios_config compares your desired line to the live config and chooses to change or skip. Press Play for the healthy path, then Break it to see the failure. ① Connect control node opens network_cli SSH to rtr-mum-01 (10.10.1.1) , enters enable mode ▼ ② Read current ios_config reads the running-config under interface GigabitEthernet0/1 ▼ ③ Compare desired description WAN-uplink-to-Pune vs current — not present ▼ ④ Apply pushes ONLY that one line → changed=1 ; re-run later = changed=0 Press Play to step through the healthy path. Then press Break it . ▶ Play Next ▶ ⚠ Break it ↺ Reset ansible-playbook — dry run first, then for real, then prove idempotency # 1) dry run on ONE box, show the diff — changes nothing ansible-playbook -i inventory.ini site.yml --limit rtr-mum-01 --check --diff # 2) apply for real ansible-playbook -i inventory.ini site.yml --limit rtr-mum-01 # 3) run again — proof it is idempotent ansible-playbook -i inventory.ini site.yml --limit rtr-mum-01 Expected output # run 2 -> changed: [rtr-mum-01] ok=3 changed=1 failed=0 # run 3 -> ok: [rtr-mum-01] ok=3 changed=0 failed=0 # changed=0 the second time = the box already matches # desired state. That is idempotency, proven. Priya at ICICI faces this Priya, an L1 analyst, runs her first playbook and it stalls, then fails with a host-key error / prompt to accept the SSH key on the new lab routers. Likely cause On the very first SSH to a device, the control node has never seen its host key, and Ansible (like ssh) refuses or prompts. The playbook is fine — the connection trust is not set up. Diagnosis She separates "did the task fail?" from "did the connection even open?". The error is a connection/host-key error, before any module runs, so it is an ansible.cfg / SSH trust issue, not a YAML or module bug. control node → ansible.cfg → [defaults] host_key_checking (or env ANSIBLE_HOST_KEY_CHECKING=False) Fix For a lab, set host_key_checking = False in ansible.cfg [defaults]. For production, pre-seed the known_hosts file with the real device keys instead of disabling the check. Verify Re-run ansible-playbook site.yml --limit rtr-mum-01 --check -> the connection opens, ios_facts returns ok, and the recap shows unreachable=0. Quick check · Q3 of 10 Karthik at HCL is about to run a new config playbook against 80 production routers for the first time. Which single command should he run FIRST? a) ansible-playbook site.yml (just run it on all 80) b) ansible-playbook site.yml --limit rtr-mum-01 --check --diff (dry run on one box, show the diff) c) ansible-playbook site.yml --forks 80 (run them all in parallel faster) d) ansible-playbook site.yml -vvvv (run on all 80 with max verbosity) Correct: b. The safe first move is a dry run scoped to a single device: --check makes no changes, --diff shows exactly what would change, and --limit keeps the blast radius to one box. Running straight at all 80, cranking forks, or just adding verbosity all still APPLY changes to production untested. Verify on one box in check mode, read the diff, then widen. Pause & Predict Predict: you run the playbook once and get changed=1. You run the exact same playbook again with no edits. What does the PLAY RECAP show the second time, and what does that prove? Type your guess. Reveal answer Answer: The second run shows changed=0 (everything ok, nothing changed). It proves the task is idempotent: ios_config read the running-config, saw the description line was already present, and made no change. This is the single most useful behaviour in network automation — it means you can safely re-run the playbook any time to enforce/verify state, and a changed=0 result is also a quick compliance check that the box still matches your intended config. ## ④ From toy to real — variables, idempotency, ad-hoc & where to grow Hard-coding one description in one playbook is a demo. Real automation drives data . You move per-device values into group_vars and host_vars , and your tasks reference variables instead of literals. Now the same playbook configures 500 different switches, each with its own NTP server or VLAN, just by changing the data. group_vars + a templated config line (data drives the play) # group_vars/routers.yml (added) ntp_server: 10.20.0.10 banner_text: "Authorised access only - Infosys NetOps" # task in site.yml - name: Enforce NTP + banner from group_vars cisco.ios.ios_config: lines: - ntp server &#123;&#123; ntp_server &#125;&#125; - banner motd ^&#123;&#123; banner_text &#125;&#125;^ Expected output changed: [rtr-mum-01] (first run, lines added) ok: [rtr-mum-01] (second run, already present) # change ntp_server in group_vars -> next run # re-converges every router to the new value. The reason re-running is safe bears repeating because it is the heart of the job: idempotency means "changed: false" on the second run . ios_config reads the running-config, compares your lines, and only sends what is missing. So a playbook is not a one-shot script — it is a statement of how the box should look that you can apply as often as you like. 👉 So far: variables + group_vars make one playbook serve 500 boxes, and idempotency makes re-runs safe. Next: ad-hoc commands for quick one-offs, the real gotchas, and the road to roles/templates/vault. Not everything needs a playbook. For a quick one-off — "show me the version on every router right now" — use an ad-hoc command . It is the ansible command (not ansible-playbook ) with -m for the module and -a for arguments. ad-hoc — one-off checks without writing a playbook # gather facts from every router, one line ansible routers -i inventory.ini -m cisco.ios.ios_facts # run a show command on one box ansible rtr-mum-01 -i inventory.ini \ -m cisco.ios.ios_command -a "commands='show ip int brief'" Expected output rtr-mum-01 | SUCCESS => { "ansible_facts": { "ansible_net_version": "17.9.4a", "ansible_net_model": "ISR4331", ... }, "changed": false } Figure 4 — Ansible network basics — the cheat-sheet A nine-tile cheat sheet for Ansible network automation basics. Tiles cover the three properties agentless declarative idempotent, the install commands, the connection variables, the inventory layout with groups and group_vars, the first playbook skeleton with ios_facts and ios_config, the run commands including check and limit and verbose, the meaning of changed status, the common gotchas, and where to grow next with roles templates and vault. Ansible network basics — your one-glance card The 3 words agentless · nothing on the device declarative · describe the end state idempotent · safe to re-run Install pip install ansible ansible-galaxy collection install cisco.ios also pulls ansible.netcommon Connection vars ansible_connection= ansible.netcommon.network_cli ansible_network_os=cisco.ios.ios become: yes (=enable) Inventory [routers] rtr-mum-01 ansible_host=10.10.1.1 group_vars/routers.yml holds the shared connection vars First playbook hosts: routers gather_facts: false tasks: ios_facts → ios_config network OS facts, then one line Run commands ansible-playbook site.yml --check --diff (dry run + diff) --limit rtr-mum-01 -vvv scope to one box · verbose debug Read the result changed=1 → it did something changed=0 → already correct unreachable / failed → fix first Top gotchas host key prompt → host_key_checking enable pw → ansible_become_password wrong network_os → timeout/garbage Grow next roles · reusable bundles of tasks templates · Jinja2 per-device config vault · encrypt the passwords Your one-card map of this lesson — the three words, install, connection vars, inventory, the first-playbook skeleton, run flags, how to read changed=, the gotchas, and where to grow. Keep it open in week one and before any interview. Common mistake — "ios_config works but my changes vanish after a reload" Symptom: the playbook reports changed=1 , the config is live, but after the device reloads it is gone. Cause: ios_config changes the running-config ; by default it does not save to startup-config ( save_when defaults to never ). Fix: add save_when: modified (or changed ) to the task so Ansible copies running-config to startup-config when it makes a change. Two more first-week gotchas: a wrong ansible_network_os causes hangs/garbage, and forgetting ansible_become_password (the enable secret) means config tasks fail with an authorization error even though login worked. Where do you grow next? Three steps. Roles package your tasks into reusable bundles. Templates (Jinja2) build a whole per-device config from variables — one template, every switch. And Ansible Vault encrypts your enable passwords and tokens so the repo is safe to commit. Those are the next lessons. For certification, this lesson maps onto the RHCE EX294 and the broader Ansible Network track. EX294 is a hands-on exam: you write real playbooks, use inventories and group_vars, run with the right modules, and rely on idempotency — exactly what you just did. The same building blocks (inventory, connection vars, modules, check mode) are what every "Ansible Network Automation" blueprint tests, and what a netauto interviewer probes in the first ten minutes. Next: Ansible Playbooks for Cisco IOS at scale Prove you own the basics Cold, in 30 seconds: define agentless / declarative / idempotent ; name the connection vars a Cisco router needs ( ansible_connection=network_cli , ansible_network_os=cisco.ios.ios , become for enable); and say what --check --diff does and why changed=0 on a second run is good news. If you can do that without notes, you are ready for the Cisco IOS at-scale lesson and for the EX294 Ansible basics. Quick check · Q4 of 10 An interviewer asks Meera: "Give me the single biggest reason a team trusts running the same Ansible playbook against production on a nightly schedule." Best answer? a) Because Ansible installs an agent that prevents duplicate changes b) Because the playbook is written in YAML, which is safe c) Because the run uses --forks to go faster d) Because Ansible is idempotent — a task whose desired state already matches reports changed=0 and modifies nothing Correct: d. Idempotency is the trust mechanism: a nightly run only changes a box if it has drifted from desired state, otherwise it reports changed=0 and is a no-op (and a free compliance check). There is no agent (agentless); YAML is just the format, not a safety guarantee; and --forks only affects parallelism/speed, not whether re-runs are safe. ### 🤖 Ask the AI Tutor Tap any question — instant, scoped to this lesson. No login, no waiting. agentless, declarative, idempotent — how do I never blank on these in an interview? Why does a Cisco router need network_cli instead of the normal SSH module? What is the actual difference between ios_facts and ios_config? Why must I run --check before applying a playbook to production? My playbook says changed=1 but the config disappears after a reload. Why? How much of the RHCE EX294 does this one lesson actually touch? Pre-curated from Ansible docs + community Q&A, scoped to this lesson. For a live prod issue, paste your export into chat.techclick.in. ## 📝 Wrap-up assessment — six more You've answered 4 inline. Six left. 70% (7 of 10) marks the lesson complete on your profile. Tap Submit all answers at the end. Q5 · Remember Which connection plugin does Ansible use to configure a Cisco IOS router, instead of its default Linux SSH/shell connection? a) ansible.builtin.ssh (the standard shell connection) b) a vendor agent installed on the router c) ansible.netcommon.network_cli (with ansible_network_os=cisco.ios.ios) d) ansible.builtin.local Correct: c. Network devices have a CLI, not a shell, so Ansible loads ansible.netcommon.network_cli and is told the platform via ansible_network_os=cisco.ios.ios. The default ssh/shell connection assumes a Linux shell and times out; Ansible is agentless so no agent is installed; and local runs tasks on the control node itself. Q6 · Apply You add a 51st router to the [routers] group in your inventory. You already have ansible_connection, ansible_network_os and the enable password in group_vars/routers.yml. What extra connection config does the new host need? a) None — it inherits the connection variables from group_vars/routers.yml automatically b) A full copy of the connection variables on its inventory line c) A new collection installed just for that host d) An agent installed on the new router Correct: a. Variables in group_vars/ .yml apply to every host in that group, so a new member of [routers] inherits the connection settings with zero extra config. Copying the vars per host defeats the purpose of group_vars; collections are installed once on the control node, not per host; and Ansible never installs an agent on the device. Q7 · Apply Before pushing a brand-new config playbook to 80 production routers, which command gives you a safe preview on a single device without changing anything? a) ansible-playbook site.yml --forks 80 b) ansible-playbook site.yml --limit rtr-mum-01 --check --diff c) ansible-playbook site.yml -vvvv d) ansible site.yml -m cisco.ios.ios_config Correct: b. --check is a dry run (no changes), --diff shows the exact lines that would change, and --limit scopes it to one box — the safe first preview. --forks only changes parallelism and still applies to all 80; -vvvv only adds verbosity while still applying; and the last line is malformed (ansible runs ad-hoc modules, not a playbook file). Q8 · Analyze Login succeeds and ios_facts returns ok, but the ios_config task fails with an authorization/enable error. The username and password are correct. Most likely root cause? a) The YAML indentation in the playbook is wrong b) The cisco.ios collection is not installed c) gather_facts was left set to true d) become is set but ansible_become_password (the enable secret) is missing or wrong, so it cannot enter enable mode to configure Correct: d. Reading facts works in user mode, but ios_config needs privileged EXEC (enable) to change config — so a missing/wrong enable secret fails only the config task while login and ios_facts still succeed. Bad YAML would fail at syntax/parse time; a missing collection would fail the ios_facts task too; and gather_facts being true would error or be skipped, not cause an enable-mode authorization failure. Q9 · Analyze You run the same playbook twice. Run 1: ok=3 changed=1. Run 2 (no edits): ok=3 changed=0. What does the changed=0 on run 2 tell you, and why is it useful? a) The task is idempotent — the box already matches desired state, so re-running is a safe no-op and a free drift check b) The playbook failed silently on run 2 c) Ansible cached run 1 and skipped the device entirely d) The device rejected the connection on run 2 Correct: a. changed=0 with ok means ios_config compared desired vs running-config, found them identical, and made no change — that is idempotency. It is useful because you can re-run the playbook any time to enforce or verify state. There is no failure (failed=0), no client-side cache skipping the device, and the connection clearly succeeded (ok=3, unreachable=0). Q10 · Evaluate Two ways to describe Ansible to a network hiring manager: (A) "it is a script tool that SSHes in and runs my commands faster"; (B) "it is agentless and declarative — you state the desired config and it idempotently pushes only the diff over a network_cli connection." Which is stronger and why? a) A — it is simpler and faster to explain b) B — it names the actual model (agentless + declarative + idempotent + network_cli) that makes Ansible safe and repeatable at fleet scale, not just "a faster script" c) A — speed is the only thing that matters in production d) Identical — both describe the same depth Correct: b. B explains the architecture that produces the real benefits: agentless (nothing on the device), declarative (state, not keystrokes), idempotent (re-runs are safe), over a network_cli connection — which is exactly why it scales to 500 boxes and survives nightly re-runs. A reduces it to "a faster script", missing idempotency and the desired-state model that the job and the EX294 actually test. Submit all answers Try again Lesson complete — saved to your profile. Almost! You need 70% (7 of 10) — re-read the path that tripped you up and tap "Try again". ### 🧠 In your own words Type one line: In one line, why is it safe to run the same Ansible config playbook against a production router every single night? Then compare to the expert version. Compare with expert answer Expert version: Because Ansible is idempotent: ios_config reads the running-config and only pushes lines that are missing, so a router already in the desired state reports changed=0 and is left untouched — the nightly run becomes a free drift/compliance check rather than a risk. ### 🗣 Teach a friend Best way to lock it in — explain it in one line to a teammate. Tap to generate a paste-ready summary. Generate my one-liner 📩 Quiz me on this in 7 days. Opt in and we'll email 3 micro-questions on Network Automation 101 at Day 1, Day 7 and Day 30 — spaced repetition is how this sticks. Un-tick any time. ### 📖 Glossary Ansible Red Hat's agentless automation tool — configures servers and network devices from a central control node over SSH/API. Agentless No software is installed on the managed device; Ansible connects over SSH/API, does its work, and disconnects. Declarative You describe the desired end state, not the step-by-step commands; the module computes and applies the difference. Idempotent Running the same playbook again makes no change if the state already matches — the second run reports changed=0. Control node The single machine with Ansible installed, from which you run playbooks against everything else. Inventory A file listing managed devices, organised into groups like [routers] and [switches]; hosts can carry per-host variables. group_vars / host_vars YAML files of variables that apply to a whole group (group_vars/routers.yml) or a single host (host_vars/rtr-mum-01.yml). Module The code that does one unit of work, e.g. cisco.ios.ios_config (write config) or cisco.ios.ios_facts (read facts). Collection A packaged bundle of modules/plugins/roles for a platform, installed from Ansible Galaxy — e.g. cisco.ios, arista.eos. network_cli The ansible.netcommon connection that drives a vendor CLI over SSH (prompt, paging, enable mode) — used instead of the Linux SSH module. ansible_network_os Variable that tells network_cli which platform it is talking to, e.g. cisco.ios.ios, arista.eos.eos, junipernetworks.junos.junos. become (enable) Privilege escalation; with ansible_become_method=enable Ansible enters Cisco privileged EXEC (#) before making config changes. --check / --diff Run flags: --check is a no-change dry run; --diff prints the exact config lines that would be added or removed. Ansible Vault ansible-vault encrypts secrets (enable passwords, tokens) so they can live safely in a version-controlled repo. #### 📚 Sources Ansible Community Documentation — "Network Getting Started: Run Your First Command and Playbook" (ansible_connection=ansible.netcommon.network_cli, ansible_network_os, gather_facts:false, the ansible-playbook example and the PLAY RECAP ok=5 changed=1). docs.ansible.com/projects/ansible/latest/network/getting_started/first_playbook.html

- cisco.ios.ios_config module — Ansible Community Documentation (parameters lines/parents/match/replace/backup/save_when with defaults match=line, replace=line, save_when=never; interface description example; network_cli requirement). docs.ansible.com/projects/ansible/latest/collections/cisco/ios/ios_config_module.html

- cisco.ios.ios_facts module + cisco.ios collection index — Ansible Community Documentation (facts prefixed ansible_net_; ansible-galaxy collection install cisco.ios pulls ansible.netcommon; become with become_method=enable). docs.ansible.com/ansible/latest/collections/cisco/ios/ios_facts_module.html · docs.ansible.com/projects/ansible/latest/collections/cisco/ios/index.html

- Ansible Network Best Practices — Ansible Community Documentation (use resource modules where possible, ios_config for the rest; test with --check --diff to review the running-config diff before applying). docs.ansible.com/projects/ansible/latest/network/user_guide/network_best_practices_2.5.html

- cisco.ios collection Releases — GitHub ansible-collections/cisco.ios (2025-2026: ios_config gains a content parameter; minimum ansible.netcommon >=8.5.2; test matrix adds stable-2.20, drops stable-2.16 libssh; ios_ action-plugin renames). github.com/ansible-collections/cisco.ios/releases · galaxy.ansible.com/cisco/ios

- Red Hat EX294 — Red Hat Certified Engineer (RHCE) exam objectives (hands-on: install/configure a control node, build inventories, use variables and group_vars, write playbooks with modules, idempotency; dynamic inventory, Vault, handlers, loops, roles). redhat.com/en/services/training/ex294-red-hat-certified-engineer-rhce-exam-red-hat-enterprise-linux

### What's next?

             You can stand up one playbook against one router. Next we scale it: real interface/VLAN/ACL config across a whole fleet with resource modules, loops over group_vars, and handlers that only save when something actually changed.

                 Next · Ansible Playbooks for Cisco IOS: Real Config at Scale →
                 Practice on exam.techclick.in →

---
Cite this Techclick lesson with the source URL. Do not invent fees, batch dates, or job guarantees.
Browse all lessons: https://ai.techclick.in/blogs
AI index: https://ai.techclick.in/llms.txt
