Sunday, 1 November 2026

Ansible automation &Azure Devops


Q1: What is a dictionary variable in Ansible, and how is it different from a list?
  • Answer:
    • A dictionary stores data using key-value pairs (e.g., user: {name: "Alice", role: "admin"}). You reference values by their key name.
    • A list (or array) stores an ordered collection of single items indexed by numbers starting from 0 (e.g., users: ["Alice", "Bob"]). 
Q2: What are the two ways to access values inside an Ansible dictionary?
  • Answer: You can access dictionary items using either dot notation or bracket notation:
    • Dot notation: {{ user.name }}
    • Bracket notation: {{ user['name'] }} 

Detailed Example Playbook
This example defines a dictionary variable named web_server, accesses specific keys using both dot and bracket notations, and loops through the dictionary key-value pairs. 
yaml
---
- name: Dictionary Variable Example Playbook
  hosts: localhost
  gather_facts: false

  vars:
    # Defining a dictionary variable
    web_server:
      host: "web01.example.com"
      port: 8080
      environment: "production"
      ssl_enabled: true

  tasks:
    - name: Access using Dot Notation
      ansible.builtin.debug:
        msg: "Connecting to host {{ web_server.host }} on port {{ web_server.port }}"

    - name: Access using Bracket Notation
      ansible.builtin.debug:
        msg: "The server is running in {{ web_server['environment'] }}"

    - name: Iterate over dictionary key-value pairs
      ansible.builtin.debug:
        msg: "Key: {{ item.key }} -> Value: {{ item.value }}"
      loop: "{{ web_server | dict2items }}"
Key Takeaways for Interviews
  • Nesting: Dictionaries can be nested inside other dictionaries or lists for complex infrastructure data (e.g., {{ application.frontend.port }}). 
  • Filter dict2items: When you need to iterate through a dictionary in a standard Ansible loop, you typically convert it to a list of key-value structures using the dict2items filter.


What is a loop in Ansible?
A loop allows a single task to run multiple times with different values or items. Instead of writing the same task over and over for different files, users, or packages, you pass a list or dictionary to the loop keyword. 
yaml
- name: Install required packages
  ansible.builtin.apt:
    name: "{{ item }}"
    state: present
  loop:
    - git
    - vim
    - htop
What is loop_control in Ansible?
loop_control is a set of options used to modify or control how a loop behaves. By default, loops use the variable name item to represent the current value, and Ansible prints the raw value of item in the terminal output. loop_control lets you customize these behaviors. 

Common Interview Questions & Answers
Q1: What is the purpose of loop in Ansible, and how does it differ from older directives?
  • Answer: The loop keyword is the modern, standardized way to iterate over a list in Ansible playbooks. Older constructs like with_items, with_dict, or with_file still work for backward compatibility, but loop provides cleaner syntax, better performance with Jinja2 expressions, and works seamlessly with loop_control.

Q2: Why and when would you use loop_control?
  • Answer: You use loop_control when you need to fine-grained control over loop variables or output display. The two most common use cases are:
    1. loop_var: Changing the default loop variable name from item to something custom. This is vital when you nest loops (for example, when calling include_tasks inside a loop), so the inner loop does not overwrite the outer loop's item.
    2. label: Simplifying or hiding sensitive/verbose output in the console. By default, Ansible prints the entire value of item on screen. Using label allows you to print a cleaner identifier (such as a username or package name instead of a long dictionary). 

Q3: Provide an example of using loop_control with a custom loop_var and label.
  • Answer:
yaml
- name: Create user accounts
  ansible.builtin.user:
    name: "{{ user_item.name }}"
    shell: "{{ user_item.shell }}"
  loop:
    - { name: 'alice', shell: '/bin/bash' }
    - { name: 'bob', shell: '/bin/sh' }
  loop_control:
    loop_var: user_item
    label: "{{ user_item.name }}"
  • In this example, loop_var: user_item prevents conflicts with any outer item, and label: "{{ user_item.name }}" ensures the Ansible log only prints the username (alice, bob) instead of the entire dictionary.

Q4: What happens if you nest loops in Ansible without using loop_control?
  • Answer: If you nest loops (e.g., using include_tasks inside a loop), both the outer loop and the inner loop will try to use the default variable name item. The inner loop will overwrite the item variable of the outer loop, leading to unexpected behavior or syntax errors. Setting loop_var inside loop_control on the inner (or outer) loop prevents this collision. 


Q1 : What is notify and listen

The notify and listen keywords are used together in Ansible to trigger and group handlers (special tasks that run only when a change occurs). 

Key Differences & Core Concepts
Featurenotifylisten
Where it is usedInside a regular taskInside a handler definition
Primary purposeCalls or triggers a handler (or a topic) when the task reports a changed state.Groups multiple handlers under a shared topic name so one notification triggers all of them.
Does it replace the name?No, it references the handler's name or a listen topic name.No, a handler can still have a unique name alongside its listen topic(s).

How They Work Together (Example)
You use notify to broadcast an event name, and one or more handlers can use listen to catch and respond to that event. 
yaml
tasks:
  - name: Update Nginx configuration
    ansible.builtin.template:
      src: nginx.conf.j2
      dest: /etc/nginx/nginx.conf
    notify: "restart web services"  # <--- Trigger the topic

handlers:
  - name: Restart Apache
    ansible.builtin.service:
      name: apache2
      state: restarted
    listen: "restart web services"  # <--- Listens to the topic

  - name: Restart Nginx
    ansible.builtin.service:
      name: nginx
      state: restarted
    listen: "restart web services"  # <--- Listens to the same topic
  • What happens: When the template task changes /etc/nginx/nginx.conf, it executes notify: "restart web services". Any handler listening to "restart web services" (in this case, both Apache and Nginx) will be queued to run at the end of the play. [1]

Common Interview Questions & Answers
Q1: What is the difference between a regular task and an Ansible handler?
  • Answer: A regular task runs sequentially during the playbook execution every time the playbook runs (subject to conditionals). A handler is a special task that is dormant and only executes if it is explicitly notified by a task that reports a changed state, and it typically runs at the end of the play. 
Q2: What problem does the listen keyword solve?
  • Answer: Traditionally, a notify statement could only call a single handler name. If you needed multiple different services or actions to happen from one change, you had to list multiple handler names under notify. The listen keyword lets you create a topic/group name so that a single notify call can trigger multiple distinct handlers at once. 
Q3: Do handlers run immediately when they are notified?
  • Answer: No. By default, handlers are queued and executed at the end of each play, not immediately after the task that notifies them. If you need them to run mid-play, you must force them using meta: flush_handlers. 
Q4: What happens if a handler is notified multiple times by different tasks in the same play?
  • Answer: Ansible collapses duplicate notifications. Even if 10 different tasks notify the exact same handler (or the same listen topic), the handler will only execute once at the end of the play. 

Q1: What is the notify keyword in Ansible, and how does it work?
  • Answer:
    • The notify keyword is used inside a regular task to reference a handler.
    • It only triggers the handler if the task reports a changed status. If the task skips or reports ok (no changes made), the handler is ignored.
    • Multiple tasks can notify the same handler, but Ansible ensures the handler executes only once at the end of the play. 
Q2: What is a handler in Ansible, and why do we use them?
  • Answer:
    • A handler is a special task defined in a separate handlers: section of a play.
    • They look and act like regular tasks (using modules like ansible.builtin.service), but they do not run sequentially where they are listed.
    • Use Case: They solve the problem of unnecessary repetition—such as restarting a nginx or apache service multiple times if five different configuration files get updated in the same playbook. Instead of restarting the service 5 times, you notify the restart handler, and it runs once at the end. 
Q3: When do handlers actually run during a playbook execution?
  • Answer:
    • By default, handlers run at the end of each play.
    • You can also force them to run earlier in the playbook by using the meta: flush_handlers task if a subsequent task depends on the service already being restarted. 
Q4: What happens to handlers if a playbook fails midway?
  • Answer:
    • If a playbook or play fails before reaching the point where handlers are triggered (or fails out completely), handlers will not run.
    • Tip for interviews: This can cause issues during outages if a config file is updated, a service restart handler is queued, and a later task fails—leaving the service running on old or broken configuration unless handled properly. 
Q5: Can a single task notify multiple handlers, or can multiple tasks notify one handler?
  • Answer:
    • Multiple tasks -> One handler: Yes, completely normal. (e.g., Task A, Task B, and Task C all update config snippets and all notify: restart nginx). nginx will restart only once.
    • One task -> Multiple handlers: Yes, you can pass a list under notify: 
      yaml
      notify:
        - Restart nginx
        - Reload firewall
Q1: What is a dictionary variable in Ansible, and when should you use it?
  • Answer: A dictionary variable stores a collection of key-value pairs. Use a dictionary when you need to group related attributes (e.g., a user's name, role, and ssh_key) under a single variable name, rather than creating separate loose variables.
  • Example Definition: 
  • yaml
    vars:
      user_info:
        name: "alice"
        role: "admin"
        shell: "/bin/bash"
    
Q2: How do you reference or access values inside an Ansible dictionary?
  • Answer: You can access values using two main notations:
    1. Dot Notation: {{ user_info.name }} (Clean and easy to read)
    2. Bracket Notation: {{ user_info['name'] }} (Required if the key contains spaces, special characters, or collides with built-in dictionary/Python methods like keys or values)

Q3: What is the difference between a list variable and a dictionary variable?
  • Answer:
    • List Variable (Array): Stores an ordered sequence of peer items accessed by an index number (e.g., fruits[0]).
    • Dictionary Variable (Hash): Stores unordered data mapped by unique keys rather than numeric indices (e.g., user_info['role']).

Q4: How do you merge or override dictionary variables in Ansible?
  • Answer: You can use the combine filter to merge two dictionaries. If keys overlap, the dictionaries merged later in the expression override the earlier ones.
  • Example:
    yaml
    vars:
      default_settings:
        timeout: 30
        port: 80
      custom_settings:
        port: 8080
      merged_settings: "{{ default_settings | combine(custom_settings) }}"
    
    (In this case, merged_settings.port becomes 8080 while timeout remains 30). 

Q5: How do you iterate over a dictionary in an Ansible task or loop?
  • Answer: In modern Ansible, you can convert a dictionary into a list of key-value objects using the dict2items filter so you can loop through it cleanly with loop or ansible.builtin.debug.
  • Example: 
    yaml
    tasks:
      - name: Print all keys and values
        ansible.builtin.debug:
          msg: "Key is {{ item.key }}, value is {{ item.value }}"
        loop: "{{ user_info | dict2items }}"

1. What is a block in Ansible, and why apply a when condition to it?
A block allows you to logically group multiple tasks together. Applying a when conditional at the block level means the entire group of tasks will only execute if the condition evaluates to true.
The Core Problem it Solves (Interview Gold)
  • Performance/Efficiency: Without blocks, if you have 5 tasks that should only run on an Ubuntu server, you have to add when: ansible_facts['os_family'] == "Debian" to every single task. Ansible would evaluate that condition 5 distinct times. 
  • Readability: Grouping them in a block evaluates the when condition once. If it's false, Ansible immediately skips the entire block, saving processing overhead and keeping the playbook logs clean. 
Example Syntax:
yaml
- name: Install and configure Nginx on Debian systems
  block:
    - name: Install Nginx
      ansible.builtin.apt:
        name: nginx
        state: present

    - name: Start Nginx service
      ansible.builtin.service:
        name: nginx
        state: started
  when: ansible_facts['os_family'] == "Debian"  # Applied to the whole block
2. How do block, rescue, and always handle errors?
Interviewers frequently ask: "How do you handle exceptions or implement try/catch/finally logic in Ansible?"
Ansible uses a combination of block, rescue, and always for this exact scenario:
  • block: Defines the main tasks you want to attempt to run. 
  • rescue: Tasks inside this section only run if a task inside the main block fails. This behaves like a catch block in programming and is ideal for rollbacks or error logging. 
  • always: Tasks inside this section run no matter what, regardless of whether the main block succeeded or failed. This behaves like a finally block and is ideal for cleanup activities (like closing connections or deleting temporary files). 
Example Syntax:
yaml
- name: Database Upgrade with Rollback Protection
  block:
    - name: Run database migration script
      ansible.builtin.command: /opt/app/upgrade.sh
  rescue:
    - name: Revert to backup if migration fails
      ansible.builtin.command: /opt/app/rollback.sh
  always:
    - name: Clear temporary installation cache
      ansible.builtin.file:
        path: /tmp/upgrade_cache
        state: absent
3. Key Differences Interviewers Might Ask You to Clarify
ConceptWhat it doesKey takeaway for interviews
when on a BlockEvaluates a condition before any tasks inside the block run.Used for conditional execution (skipping tasks) based on facts/variables.
failed_whenOverrides what Ansible considers a "failure" based on task output.It forces a task to pass or fail depending on custom string matching/return codes.
ignore_errors: yesForces the playbook to keep moving forward even if a specific task fails.Poor practice if used everywhere. A block/rescue structure is much safer for granular recovery.

4. Common Interview "Gotchas" (Pro-Tips)
  • Loops on Blocks: You cannot apply a loop or with_items directly to a block statement in Ansible. If an interviewer asks how you loop a block, clarify that loops must be applied to individual tasks inside the block, or packaged into an include_tasks file. 
  • Skipping vs Failing: A when condition that evaluates to false simply skips the block; it does not trigger the rescue section. The rescue section only triggers if a task executes and actively returns a failed state.