Ansible Automation Case Studies & Practical Application 4 — Questions and Answers
Question 1: A playbook task uses the shell module to run a script that always returns exit code 0, even on failure, but prints 'ERROR' to stdout on failure. How should the task detect failure?
- Set ignore_errors: true and check the output manually
- Use failed_when with a condition checking the registered output for 'ERROR' (Correct answer)
- Replace shell with command module which handles exit codes differently
- Use the assert module after the task
Correct answer: Use failed_when with a condition checking the registered output for 'ERROR'
failed_when allows custom failure conditions using registered output, overriding the default exit-code-based failure detection.
Question 2: During a playbook run against 100 hosts, tasks must complete on all hosts before any host moves to the next task. Which playbook setting enforces this behavior?
- serial: 100
- strategy: linear (the default) (Correct answer)
- strategy: free
- async: 0
Correct answer: strategy: linear (the default)
The default linear strategy ensures all hosts complete each task before Ansible moves on to the next task in the play.
Question 3: A company wants to reuse a complex deployment procedure across multiple projects without copy-pasting playbooks. Which Ansible feature enables packaging and sharing this reusable automation?
- Playbook includes with include_playbook
- Ansible Collections or Roles distributed via Ansible Galaxy (Correct answer)
- Copying the playbook directory to each project
- Using group_vars to share logic across projects
Correct answer: Ansible Collections or Roles distributed via Ansible Galaxy
Ansible Roles and Collections can be published to Ansible Galaxy and installed as dependencies, enabling consistent reuse across projects.
Question 4: A playbook must perform cleanup tasks (remove temp files) even when earlier tasks fail. Which Ansible block structure guarantees cleanup runs regardless of errors?
- rescue block
- always block inside a block/rescue/always structure (Correct answer)
- handlers with listen directives
- Setting force_handlers: true at play level
Correct answer: always block inside a block/rescue/always structure
The always section of a block/rescue/always structure runs unconditionally, making it ideal for guaranteed cleanup tasks.
Question 5: An Ansible playbook needs to retrieve a secret from HashiCorp Vault at runtime and use it as a variable. What is the correct mechanism?
- Write the secret to a temp file and read it with lookup('file')
- Use a lookup plugin such as lookup('hashi_vault', ...) to fetch the secret dynamically (Correct answer)
- Store the Vault token in plain text in group_vars
- Use ansible-vault to re-encrypt the HashiCorp secret locally
Correct answer: Use a lookup plugin such as lookup('hashi_vault', ...) to fetch the secret dynamically
Ansible lookup plugins, including the hashi_vault plugin, retrieve external data at play time and inject it as a variable.
Question 6: A team uses dynamic inventory to pull host lists from AWS EC2. When running the playbook, hosts appear but Ansible cannot connect via SSH. What is the most likely cause to investigate first?
- The dynamic inventory script has a bug in the JSON output format
- Security group rules or SSH key configuration blocking access on port 22 (Correct answer)
- Ansible does not support dynamic inventory with EC2
- The playbook is missing gather_facts: true
Correct answer: Security group rules or SSH key configuration blocking access on port 22
Dynamic inventory correctly resolves host IPs, but network-level issues like security groups or missing SSH keys prevent the actual connection.
Question 7: A playbook applies the same 10-step configuration to both RHEL and Ubuntu servers in one run. Tasks 3 and 7 differ by OS. What is the cleanest way to handle the OS-specific steps?
- Write two separate playbooks and run them separately
- Use when conditionals on tasks 3 and 7 to branch by ansible_os_family
- Create two inventory groups and duplicate all 10 tasks per group
- Use import_tasks with different task files per OS, wrapping only those two tasks (Correct answer)
Correct answer: Use import_tasks with different task files per OS, wrapping only those two tasks
import_tasks or include_tasks with OS-specific task files keeps shared steps unified while isolating the divergent steps cleanly.
A playbook task uses the shell module to run a script that always returns exit code 0, even on failure, but prints 'ERROR' to stdout on failure.
How should the task detect failure?