Interview Prep: Core Concepts¶
What is idempotency, and why does it matter operationally?¶
Short answer: Running the same operation multiple times produces the same end state as running it once — no duplicated side effects on repeat runs.
Detailed: Ansible modules follow a check-then-act pattern: compare current state to desired state, and only change what actually differs. This is what makes "just run the playbook again" a safe default in production instead of a gamble. Full mental model: Idempotency.
Common misconception: That playbooks are idempotent. They're not — modules are (or aren't); a playbook is only as idempotent as the modules its tasks call. A shell task inside an otherwise-idempotent playbook is still not idempotent.
Senior follow-up: "How would you audit an existing 300-task playbook for non-idempotent tasks without reading every line by hand?" — run it twice and diff the changed counts in the recap; any task reporting changed on the second run of an unmodified target is a candidate.
What is Ansible's variable precedence order, from highest to lowest?¶
Short answer: -e (extra vars) highest, role defaults/main.yml lowest. In between, roughly from high to low: include and role parameters, set_fact/registered vars, include_vars, task vars, block vars, role vars/, play vars_files/vars, facts, host_vars, group_vars/<group>, group_vars/all, and inventory-file vars.
Detailed: Full order and the reasoning behind it (broad/overridable at the bottom, narrow/deliberate at the top): Variable Precedence.
Common misconception: That group_vars beats role vars/main.yml — it's the opposite; role vars (not defaults) outranks group_vars. A second one: that inventory variables beat facts. Gathered facts outrank every host_vars/group_vars level, which is why naming your own variable ansible_hostname goes wrong.
Senior follow-up: "Two roles are included in the same play and each defines a defaults/main.yml value for a same-named variable — which wins, and why is that a design smell?" — the last-included role's default typically applies in practice due to load order, but the real answer is that unnamespaced role variables colliding at all is the actual problem; see Role Variables and Interfaces.
What is the check-then-act pattern, and which layer implements it — the playbook or the module?¶
Short answer: The module. A playbook just calls modules in order; whether a given call is safe to re-run depends entirely on whether that module checks state before acting.
Detailed: See Modules for the mechanism, and Build a Custom Module for what implementing it actually looks like in code.
Senior follow-up: "You've written a custom module — what specifically makes it check-mode-safe, beyond declaring supports_check_mode=True?" — the code must explicitly branch on module.check_mode and skip the actual write; declaring support without honoring it is worse than not supporting it, because it looks safe and isn't.
What's the practical difference between command and shell?¶
Short answer: command runs a program directly with no shell involved — no pipes, redirects, or injection risk from shell metacharacters. shell runs the argument through /bin/sh, enabling shell features at the cost of injection risk.
Detailed: Full comparison, including raw and script, and a decision tree: Command vs. Shell vs. Raw vs. Script.
Common misconception: That either one is idempotent because it's "inside a playbook." Neither is, by default — both need creates/removes/changed_when to behave safely on a re-run.
Senior follow-up: "A shell task builds its command string from a variable that ultimately traces back to user input somewhere upstream — what's the actual risk, and how do you eliminate it, not just mitigate it?" — command injection; eliminate it by switching to command (no shell) or a real module, not by trying to sanitize the string.
Next¶
Continue to Architecture & Performance.