Friday, 28 August 2026

Ansible Automation on Exacc and Oracle linux 8

 In complex enterprise deployments, managing infrastructure dynamically requires a balance between speed and data discovery. Testing bleeding-edge or prerelease playbooks using the Ansible dev branch alongside targeted systems—such as Oracle Linux 8 on Oracle Exadata Cloud@Customer (ExACC)—demands highly efficient automation strategies.

Below is an overview of why gather_facts is critical, an example playbook explicitly tailored for ExACC Oracle Linux 8, and an interview preparation guide. [

Understanding the Use Case
  • The Ansible dev Branch: Running the latest development branch (main / devel from GitHub) allows you to use cutting-edge features and optimizations before they stable out into official releases.
  • ExACC & Oracle Linux 8: Oracle's Exadata Cloud@Customer runs enterprise-grade workloads. Because these are critical database ecosystems, gathering bloated server facts over high-security enterprise networks can slow down deployments. Therefore, filtering facts or using target subsets becomes a requirement rather than an option. 

Practical Example: ExACC Oracle Linux 8 Playbook
This playbook disables global fact collection to save time (gather_facts: false) and uses the setup module directly with gather_subset. It pulls only minimal network and hardware parameters to dynamically verify that the ExACC VM node meets Oracle Database requirements. 
yaml
---
- name: Verify ExACC Oracle Linux 8 Pre-requisites via Dev Branch
  hosts: exacc_nodes
  gather_facts: false  # Explicitly disabled globally for performance
  become: true

  tasks:
    - name: Dynamically gather ONLY minimal and network subsets
      ansible.builtin.setup:
        gather_subset:
          - '!all'      # Exclude everything by default
          - 'min'       # Include core OS variables (distribution, version)
          - 'network'   # Include IP addresses and interface info
      register: target_facts

    - name: Validate that target is indeed Oracle Linux 8
      ansible.builtin.assert:
        that:
          - ansible_facts['distribution'] == "OracleLinux"
          - ansible_facts['distribution_major_version'] == "8"
        fail_msg: "Target server is not running Oracle Linux 8. Aborting configuration!"
        success_msg: "Validated ExACC Node: Operating system is Oracle Linux 8."

    - name: Allocate HugePages based on gathered memory parameters
      ansible.builtin.sysctl:
        name: vm.nr_hugepages
        value: "16384"
        state: present
        reload: true
      when: ansible_facts['distribution'] == "OracleLinux"
Use Cases & Scenarios
  • ExACC Database Kernel Tuning: Automatically tuning kernel boundaries (sysctl.conf) on Oracle Linux 8 based on available RAM facts (ansible_memtotal_mb). 
  • Network Isolation Checks: Evaluating ansible_interfaces on multi-homed ExACC infrastructure to ensure the Oracle Client/Application networks match strict architecture requirements. 
  • Ansible Dev Feature Testing: Checking performance improvements of the setup module's execution speed directly from the Ansible source repository against dense hardware architectures.
Q1: Why would you set gather_facts: false globally and call the setup module manually?
Answer: Speed and efficiency. In scale-out environments or complex enterprise systems like Oracle ExACC, gathering every default system fact (including deep storage paths, mounting trees, and virtual interfaces) introduces massive overhead and latency. Disabling it globally allows us to use gather_subset inside specific plays to isolate only the exact data variables we need—such as network or hardware—cutting execution time significantly. 
Q2: What is the behavior of using the !all parameter in gather_subset?
Answer: The !all modifier serves as a complete reset. It tells the Ansible setup module to drop all optional collection blocks. If we pass - '!all' followed by - 'network', Ansible bypasses storage, hardware, or virtualization detection, compiling purely the network footprints. 
Q3: If you are tracking a bug in the Ansible dev branch regarding gather_facts on Oracle Linux 8, how do you debug the raw JSON payload?
Answer: You can execute an ad-hoc Ansible command running the setup module directly against your target host group. By running:
ansible exacc_nodes -m setup
Ansible drops the raw, unparsed JSON fact array directly into the terminal console. This permits real-time verification of whether a feature introduced on the dev branch is properly collecting a platform-specific fact on an Oracle environment. [
Q4: How do you differentiate a custom fact from a core system fact on Oracle Linux?
Answer: Core system facts are mapped natively by the underlying setup framework. Custom facts are managed manually by dropping static or executable scripts inside /etc/ansible/facts.d/ on the remote ExACC machine (ending in .fact). When gather_facts executes, these application-specific files are parsed and aggregated into the ansible_local variable dictionary block. 

Question:
"How would you use the ansible.builtin.command module within a Git-driven development workflow to manage or query LDAP integration on an Oracle Linux 8 Exadata Cloud@Customer (ExaCC) environment? Provide a production-ready example and explain the architectural use cases."

Interview Answer (Structured & Comprehensive)
1. Core Architecture & Workflow Integration
To safely manage database infrastructures like Oracle Exadata Cloud@Customer (ExaCC) running Oracle Linux 8 (OL8), we implement a Git branching strategy. [1]
  • devel/dev Branch: Used to stage and safely test non-idempotent system validations or administrative tasks.
  • Execution Module: While native collections like community.general.ldap_entry exist for object management, low-level operating system tasks—such as testing client authentication, executing raw ldapsearch scripts, or interacting with localized System Security Services Daemon (sssd) caches—rely directly on the ansible.builtin.command or ansible.builtin.shell modules. 
2. Production Playbook Example
This playbook targets an Oracle Linux 8 ExaCC DB node. It securely runs an ad-hoc LDAP lookup using system binaries to ensure the node is communicating properly with the centralized directory before executing further database automation. 
yaml
---
# Name: validate_exacc_ldap.yml
# Branch Status: Staged & Verified in Git 'dev' branch
- name: Validate LDAP Integration on ExaCC Oracle Linux 8 Nodes
  hosts: exacc_db_nodes
  become: true
  gather_facts: true

  vars:
    # Production LDAP variables securely passed or vaulted
    ldap_server_uri: "ldaps://directory.enterprise.internal:636"
    ldap_base_dn: "ou=ExaCC,ou=Databases,dc=enterprise,dc=internal"
    ldap_bind_dn: "cn=ansible_svc,ou=ServiceAccounts,dc=enterprise,dc=internal"
    # Reference password securely via Ansible Vault or AAP Credential variable
    ldap_bind_pw: "{{ vaulted_ldap_bind_password }}" 
    test_user_uid: "ora_db_admin"

  tasks:
    - name: Ensure openldap-clients utilities are present on Oracle Linux 8
      ansible.builtin.dnf:
        name: openldap-clients
        state: present

    - name: Query LDAP Server via Command Module
      ansible.builtin.command: >
        ldapsearch -x 
        -H "{{ ldap_server_uri }}" 
        -D "{{ ldap_bind_dn }}" 
        -w "{{ ldap_bind_pw }}" 
        -b "{{ ldap_base_dn }}" 
        "(uid={{ test_user_uid }})"
      register: ldap_search_result
      # Use changed_when: false because raw read-only queries are inherently idempotent 
      changed_when: false
      failed_when: 
        - ldap_search_result.rc != 0
        - "'numResponses: 0' in ldap_search_result.stdout"

    - name: Log LDAP connectivity and schema status
      ansible.builtin.debug:
        msg: "LDAP Verification Passed! Found user DN: {{ ldap_search_result.stdout_lines | select('match', '^dn:') | list }}"
3. Core Enterprise Use Cases
Use CaseImplementation StrategyPurpose for ExaCC / Oracle 8
Pre-Flight OS VerificationRunning raw ldapsearch or getent passwd through ansible.builtin.command.Ensures the OS layer can correctly resolve centralized Oracle Database Administrators (DBAs) mapping via external directory services before kicking off major patch installations.
SSSD Cache ManagementInvoking sss_cache -E or restarting sssd using the command/service framework.Flushes localized client credentials on the ExaCC node when directory access rules change, ensuring immediate authorization synchronization.
Failover Network DiagnosticsExecuting shell routines targeting alternative LDAP replica nodes.Ensures database nodes can gracefully gracefully survive an primary Active Directory / LDAP server outage.

4. Why ansible.builtin.command instead of ansible.builtin.shell?
When answering this question, emphasize security best practices:
  • Command Module (Preferred): It does not invoke a guest shell (/bin/sh). This means shell variables, wildcards (*, ?), and pipe configurations (\|) are disabled. This inherently shields your ExaCC cluster from dangerous shell injection vulnerabilities, especially when dealing with high-privilege credentials like LDAP Bind passwords. 
  • Shell Module (Fallback): Use only when the task strictly requires environment variables, path evaluations, or output piping that cannot be handled via native Ansible registers.


Question: *"We are deploying a new microservices cluster on Oracle Exadata Cloud@Customer (ExaCC) compute nodes running Oracle Linux 8. As part of our CI/CD pipeline pulling from the Ansible development branch, we need to automate LDAP user provisioning/validation. We are using a legacy script via ansible.builtin.command because native LDAP modules aren't available in our tight environment. 
How would you ensure this task accurately reports change states (changed_when: true) and catches complex execution errors (failed_when) instead of relying blindly on basic OS exit codes? Provide a real-world playbook example."*

 The Perfect Answer
1. Direct Strategic Overview
"To solve this on Oracle Exadata Cloud@Customer (ExaCC), we must account for the fact that raw shell scripts or terminal commands (like ldapmodify or ldapsearch) don't naturally communicate idempotency to Ansible. They almost always return an exit code of 0 even if no updates were actualized, or they might emit an exit code of 1 for non-critical warnings. 
By registering the standard output (stdout) of the command, we can use changed_when to intercept the string confirmation showing an actual LDAP modification occurred, and failed_when to declare a task failure if specific database error substrings or LDAP bind exceptions occur—overriding default exit code behaviors." 
2. Production Playbook Example (ExaCC / Oracle Linux 8)
yaml
---
- name: ExaCC Oracle Linux 8 LDAP Integration Update
  hosts: exacc_db_nodes
  become: true
  vars:
    ldap_server: "ldaps://directory.internal.exacc"
    bind_dn: "cn=oraclerm,dc=enterprise,dc=com"
    user_uid: "ora_app_dev"

  tasks:
    - name: Modify or Sync LDAP User Account State for Oracle DB Access
      ansible.builtin.command: >
        ldapmodify -x -H {{ ldap_server }} 
        -D "{{ bind_dn }}" 
        -w "{{ vault_ldap_bind_password }}" 
        -f /u01/app/oracle/admin/ldap_user_sync.ldif
      register: ldap_result
      
      # 1. Force Ansible to mark 'changed: true' ONLY if LDAP server actually modified entries
      changed_when: 
        - "'modifying entry' in ldap_result.stdout"
        - "'0 changes executed' not in ldap_result.stdout"

      # 2. Trigger failure on custom error strings, even if exit code indicates 0
      failed_when:
        - "ldap_result.rc != 0 or 'LDAP_ERR' in ldap_result.stderr"
        - "'Server down' in ldap_result.stderr or 'Invalid credentials' in ldap_result.stderr"

    - name: Debug LDAP synchronization status for validation
      ansible.builtin.debug:
        msg: "LDAP Modification Executed Successfully? {{ ldap_result.changed }}"
 Key Interview Discussion Points & Deep Dive
Why changed_when: true is crucial here
By default, the ansible.builtin.command module flags a task as changed every single time it runs, because Ansible doesn't inherently understand what happened inside an isolated executable. 
  • Specifying conditions via changed_when prevents false positives in your CI/CD pipeline.
  • It ensures downstream handlers (like reloading the Oracle Names/LDAP client daemon or SSSD) only fire when an absolute modification has passed. 
Why failed_when is required for Oracle/LDAP utilities
Many Enterprise directory tools or custom database wrapper scripts handles errors internal to their software layers and might log a critical failure (e.g., Result: Insufficient access rights) directly to stdout/stderr while still closing out with a safe system exit code rc: 0. Using failed_when forces Ansible to parse textual strings to guarantee true infrastructural reliability. 
 Practical Use Cases on ExaCC (Oracle Linux 8)
  1. SSSD/LDAP Local Host Caching Sync: Triggering an entry flush or dynamic host-group sync across hundreds of Exadata cluster nodes without triggering false config drift alarms. 
  2. Oracle Wallet or Database User Authentication Mapping: Running Oracle's centralized automated provisioning utilities (mkstore, orapwd) where tracking exact changes inside secure binaries is normally invisible to config managers. 
  3. Automated Rollover of Bind Secrets: Verifying active directory binds during massive security credential updates. 

 Follow-up Questions to Expect
  • Interviewer: "What happens if you use a list format under failed_when versus a single-line Jinja evaluation string?"
  • Candidate Answer: "When a list format is provided to failed_when or changed_when, Ansible evaluates the list using an implicit AND logic (all items must evaluate to true to trigger failure). If I need an OR evaluation sequence, I must format it explicitly as a single string line using the or keyword (e.g., failed_when: "result.rc != 0 or 'Error' in result.stderr")." 



Ansible Privilege Escalation: Fully Qualified Become Plugins
In Ansible, managing privilege escalation has transitioned toward using Fully Qualified Collection Names (FQCN) to target specific plugins explicitly. Using become_method: ansible.builtin.sudo instead of the shorthand sudo ensures that your playbooks directly utilize the core privilege escalation plugin provided natively by ansible-core. [1]
When combined with the ansible.builtin.command module, it allows tasks to execute raw binaries securely with elevated access without spawning an unnecessary system shell. [1, 2]

Playbook Blueprint: Oracle Linux 8 on ExaCC (Exadata Cloud@Customer)
In an Exadata Cloud@Customer (ExaCC) infrastructure running Oracle Linux 8, administrators login as the unprivileged opc or oracle user but must transition to grid or root to run specific infrastructure tools (like crsctl or dbcli).
The blueprint below shows how to write a structured playbook targeting a development environment (dev branch) using FQCNs. [1, 2]
yaml
---
- name: Execute Infrastructure Checks on ExaCC Dev Branch
  hosts: exacc_dev_servers
  gather_facts: false
  remote_user: opc  # Default unprivileged cloud user on Oracle Cloud / ExaCC

  tasks:
    - name: Verify Oracle Clusterware (GI) Status using FQCN Sudo
      ansible.builtin.command:
        cmd: "/u01/app/19.0.0.0/grid/bin/crsctl check crs"
      
      # Privilege Escalation Configuration
      become: true                             # Activates the privilege mechanism
      become_method: ansible.builtin.sudo     # Explicitly uses the FQCN core sudo plugin
      become_user: root                        # Target user to execute the command as

      register: crs_status
      changed_when: false                      # Read-only operation; prevents false 'changed' states

    - name: Output Clusterware Status
      ansible.builtin.debug:
        var: crs_status.stdout_lines
Real-World ExaCC Oracle Linux 8 Use Cases
  1. Oracle Grid Infrastructure Management: Running commands such as crsctl or srvctl requires escalation to root or grid users to check cluster state, restart node applications, or modify cluster resources.
  2. Exadata Storage Cell & Local Patching Tasks: Executing local diagnostics tools (dbcli, oakcli, or checking local /var/log/messages) which are heavily locked down on hardened Oracle Linux 8 ExaCC database nodes.
  3. Database Parameter Tuning: Modifying kernel constraints specifically required for Oracle Databases (e.g., adjusting HugePages allocation or tuning /etc/sysctl.conf rules) which cannot be performed under default cloud user logins (opc).

Interview Q&A Matrix
Q1: Why should you use become_method: ansible.builtin.sudo instead of just writing become_method: sudo?
Answer: While shorthand sudo still works as a default baseline, explicitly declaring ansible.builtin.sudo adheres to modern Ansible best practices using Fully Qualified Collection Names (FQCN). It eliminates namespace collisions if third-party collections implement a custom sudo plugin and guarantees predictable execution paths across various environments and upstream version upgrades. 
Q2: What is the primary operational difference between using ansible.builtin.command and ansible.builtin.shell when escalation is enabled?
Answer: The ansible.builtin.command module executes the target binary directly on the remote node without starting an underlying shell processor (like sh or bash). This is more secure and performant because it prevents shell injection vulnerabilities and respects strict enterprise security auditing tools. Conversely, ansible.builtin.shell routes the command through a shell interpreter, making it slower and less secure, though necessary if environment variables, pipes (|), or redirection (>) are required. 
Q3: On an ExaCC Oracle Linux 8 server, your task fails with "Missing sudo password" even though become_method is correct. How do you resolve this within Ansible?
Answer: This happens because the target node's /etc/sudoers file requires a password for that user execution path. You can resolve this by: 
  • Passing the runtime flag --ask-become-pass (or -K) when executing the playbook.
  • Using the ansible_become_pass variable securely injected via Ansible Vault to dynamically provide the decryption key. 
Q4: How does Ansible safely transition code execution when become: true and become_method: ansible.builtin.sudo are active?
Answer: Ansible connects over SSH as the initial remote_user. It then builds a temporary python executable block, transfers it to a secured remote directory (e.g., /tmp), and wraps its execution inside a sudo wrapper string (sudo -u <become_user> python <script>). The code runs safely under the target identity and streams JSON responses back to the controller machine. [

The ansible.builtin.set_fact module allows you to define or change variables dynamically at runtime during playbook execution. These variables are assigned to specific hosts and persist across subsequent plays within the same playbook run. 
Here is a comprehensive breakdown structured as an executive-level interview preparation guide. It features an advanced enterprise architecture scenario: Oracle Exadata Cloud@Customer (ExaCC) running Oracle Linux 8 (OL8).

 Core Concept: set_fact vs Other Variables
  • vars / vars_files: Static. Evaluated before the playbook starts executing tasks.
  • register: Automatically captures the raw, exact output of a specific task (e.g., standard output, exit codes).
  • set_fact: Dynamic. Assigns custom, evaluated, or cleaned-up data to a variable mid-execution. It can use Jinja2 filters to process raw register data. 

 Real-World Scenario: Oracle ExaCC on Oracle Linux 8
The Challenge:
When deploying or patching Oracle Databases on Exadata Cloud@Customer (ExaCC) running Oracle Linux 8, you often manage cluster environments (RAC). Before running database commands, you must dynamically locate the active Oracle Grid Infrastructure Home (GRID_HOME) and parse the host's specific allocation of hugepages from system files. You cannot hardcode these because node storage mounts and memory topologies vary across virtual machines (VM clusters).
Ansible Playbook Example
yaml
---
- name: Post-Provisioning Configuration for Oracle ExaCC Nodes
  hosts: exacc_nodes
  become: yes
  gather_facts: yes

  tasks:
    # 1. Run a command to find where the Grid Infrastructure is mounted on Oracle Linux 8
    - name: Query Oracle Grid Infrastructure Home path
      ansible.builtin.shell: |
        awk -F: '/^\+ASM/ {print $2}' /etc/oratab | head -n1
      register: grid_home_query
      changed_when: false

    # 2. Use set_fact to sanitize the input and create structured runtime variables
    - name: Dynamically set Oracle Environment facts
      ansible.builtin.set_fact:
        oracle_grid_home: "{{ grid_home_query.stdout | trim }}"
        is_exadata_node: "{{ 'exa' in ansible_facts['hostname'] | lower }}"
        target_hugepages: "{{ (ansible_facts['memtotal_mb'] * 0.6 / 2) | int }}"

    # 3. Use the newly created facts to execute subsequent environment tasks
    - name: Verify Grid Home path discovery
      ansible.builtin.debug:
        msg: "Targeting Grid Home at {{ oracle_grid_home }}. Exadata Check: {{ is_exadata_node }}"

    # 4. Use the dynamic hugepages calculation to configure Oracle Linux 8 sysctl kernel parameters
    - name: Configure Linux Hugepages for Oracle Database
      ansible.posix.sysctl:
        name: vm.nr_hugepages
        value: "{{ target_hugepages }}"
        state: present
        reload: yes
      when: is_exadata_node | bool and oracle_grid_home != ""
 Key Production Use Cases for set_fact
  • Dynamic Path Discovery: Locating database binaries, listener configs, or ORACLE_HOME targets that differ across nodes. 
  • Mathematical Tuning on the Fly: Computing kernel configs (like vm.nr_hugepages or shmmax) at runtime as a precise percentage of the target server's physical memory. 
  • Complex Feature Flags: Pre-evaluating complex Jinja2 statements into clean boolean flags (is_exadata_node) to keep subsequent when conditions short and readable. 
  • State Accumulation: Appending data to lists over loops to build consolidated arrays (e.g., accumulating a list of databases running on a cluster node). 
  • Cross-Host Fact Sharing: Combining set_fact with hostvars to pass information from Node 1 over to Node 2 during a rolling upgrade. 

Q1: What is the main difference between register and set_fact?
Answer: register takes the entire raw metadata output from an executed task and dumps it into a variable. set_fact is used to selectively extract, transform, or calculate data (often using the data from a registered variable) and save it as a cleanly formatted host variable for later use. 
Q2: What is the variable precedence of a fact defined by set_fact?
Answer: Variables set via set_fact have a very high precedence (rank 19 out of 22 in modern Ansible precedence hierarchies). They will override variables defined in group_vars, host_vars, playbook vars, and role defaults. They can only be overridden by explicit extra vars (-e) passed at the command line. 
Q3: Do variables declared with set_fact persist across different plays in a playbook?
Answer: Yes. Facts created via set_fact are scoped to the host and persist for the remainder of the entire playbook run, across multiple plays. However, they do not persist in subsequent, separate playbook executions unless cacheable: true is set and a backend fact cache (like Redis) is active. 
Q4: How would you use set_fact to handle an environment matrix conditionally without making your code messy?
Answer: Instead of using five different set_fact tasks with separate when: conditionals, the best practice is to define a lookup dictionary containing database settings for Oracle Linux 7 vs Oracle Linux 8. Then, use a single set_fact task to extract the proper configuration dynamically by indexing into that dictionary using ansible_facts['distribution_major_version']

An excellent scenario-based interview question. The ansible.builtin.lineinfile module is heavily tested in DevOps interviews because it requires an understanding of idempotency and regular expressions. [
When asked about it in the context of an Oracle 8 Exadata Cloud@Customer (ExaCC) infrastructure orchestration pipeline using a dev branch, the interviewer is looking to see how you handle infrastructure-as-code (IaC) governance alongside surgical OS-level modifications.

Interview Question & Answer Blueprint
Q: How would you use ansible.builtin.lineinfile in a dev branch workflow to configure an Oracle 8 ExaCC database node? Provide a practical use case and a playbook snippet.
A: High-Level Explanation
"In an Oracle 8 ExaCC (Exadata Cloud@Customer) environment, OS configurations (like /etc/security/limits.conf or kernel parameters) must match strict Oracle Maximum Availability Architecture (MAA) guidelines.
We use a Git branching strategy where features are tested in a dev branch first. For surgical, single-line configuration changes—such as tweaking an Oracle user shell limit or adjusting a database parameter file—we use ansible.builtin.lineinfile. It ensures the requirement is idempotent, meaning it will only modify the file if the exact string or pattern isn't already perfectly configured." 

Key ExaCC Use Cases for lineinfile
  • Oracle User Resource Limits: Adjusting nofile (number of open files) or nproc limits in /etc/security/limits.conf.
  • Kernel & HugePages Tuning: Modifying lines in /etc/sysctl.conf for optimized Oracle 19c/23c database performance on RHEL/OEL 8.
  • Storage Mount points: Surgically adding specific NFS or local Exadata storage mount configurations to /etc/fstab.
  • SSH Hardening: Ensuring required security compliances (PermitRootLogin no) are forced on database VMs. 

Playbook Example (Simulated in a dev branch pipeline)
This example edits the /etc/security/limits.conf file to configure specific oracle user limits on an Oracle Linux 8 ExaCC compute node.
yaml
---
# Environment: Dev Branch Testing
# Target: ExaCC Oracle 8 Database VM Node
- name: Tune Oracle 8 ExaCC Node Configurations
  hosts: exacc_dev_db_nodes
  become: true
  vars:
    oracle_max_nofile: 65536

  tasks:
    - name: "DEV: Adjust Oracle user nofile soft limit in limits.conf"
      ansible.builtin.lineinfile:
        path: /etc/security/limits.conf
        regexp: '^oracle\s+soft\s+nofile'
        line: "oracle          soft    nofile          {{ oracle_max_nofile }}"
        state: present
        backup: true  # Mandatory for production/ExaCC safety
      tags: [dev, tuning]

    - name: "DEV: Disable Transparent HugePages in GRUB if missing"
      ansible.builtin.lineinfile:
        path: /etc/default/grub
        regexp: '^(GRUB_CMDLINE_LINUX=.*)transparent_hugepage=never(.*)$'
        line: '\1\2 transparent_hugepage=never'
        backrefs: true  # Prevents duplicating the line if it partially matches
        state: present
      notify: Regenerate Grub
      tags: [dev, kernel]

  handlers:
    - name: Regenerate Grub
      ansible.builtin.command: grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg
Critical Interview Deep-Dive Points (To Ace the Interview)
  1. Why lineinfile instead of template?
    • Interview Answer: Use lineinfile when you only need to change one specific line inside a massive, vendor-managed file (like an ExaCC default system config). Using a template module would overwrite the entire file, which could dangerously strip out Exadata-specific configurations injected by Oracle's cloud tooling. 
  2. The Importance of regexp and backrefs
    • Interview Answer: Without regexp, lineinfile just appends your line to the end of the file every time a variable changes, creating messy duplicates. regexp ensures it finds and replaces the target. Setting backrefs: true ensures that if the regex doesn't match, the file is left entirely untouched. 
  3. dev Branch Validation
    • Interview Answer: In our CI/CD pipeline, the dev branch playbook runs with the --check --diff flags against a staging/dev ExaCC environment. The --diff output explicitly captures what line changes will occur before they hit the live development environment. 

Direct Answer First
Using vars_file: other_var/{{env}}.yml inside an Ansible dev branch is a production-grade strategy for managing environment-isolated architecture dynamically. By passing the environment identifier via the command line (e.g., -e env=dev_exacc), Ansible injects the dynamic string to evaluate and pull precise parameters for environments like Oracle Exadata Cloud@Customer (ExaCC) running Oracle Linux 8. [1, 2, 3]

Production Use Cases
  • Infrastructure Right-Sizing: Scaling ExaCC cluster parameters like VM shapes, CPU core counts, or memory allocations proportionally higher in prod.yml than in dev.yml.
  • Dynamic Grid & DB Configurations: Feeding environment-specific Oracle Home paths, SID nomenclature (e.g., ORCLDEV vs ORCLPROD), and structural grid disk layouts based on the file parsed.
  • Separation of Access: Isolating API endpoints, security groups, and cloud subnet IDs within distinct, encrypted variable structures.

Comprehensive Code Example
Context: Deploying database configuration configurations onto an ExaCC Oracle Linux 8 Environment. [1]
1. Directory Structure (dev branch)
text
ansible-project/ (dev branch)
├── playbook.yml
└── other_var/
    ├── dev_exacc.yml
    └── prod_exacc.yml
Use code with caution.
2. Environment Variable File (other_var/dev_exacc.yml)
yaml
---
# Environment specific definitions for Dev ExaCC
oracle_version: "19.0.0"
oracle_os_user: "oracle"
oracle_base: "/u01/app/oracle"
oracle_home: "/u01/app/oracle/product/19.0.0/dbhome_1"
oracle_sid: "EXACCDV1"
# ExaCC Specific parameters
cluster_name: "exacc_dev_cluster"
cpu_cores: 4
Use code with caution.
3. Main Playbook (playbook.yml)
yaml
---
- name: Configure Oracle 19c DB on Exadata Cloud@Customer (ExaCC)
  hosts: exacc_servers
  become: yes
  
  vars_files:
    - "other_var/{{ env }}.yml"  # Dynamically loads based on -e env=<value>

  tasks:
    - name: Ensure targeted host is running Oracle Linux 8
      ansible.builtin.assert:
        that:
          - ansible_distribution == "OracleLinux"
          - ansible_distribution_major_version == "8"
        fail_msg: "Target OS must be Oracle Linux 8!"

    - name: Create Oracle Inventory Directories
      ansible.builtin.file:
        path: "{{ oracle_base }}"
        state: directory
        owner: "{{ oracle_os_user }}"
        group: oinstall
        mode: '0755'

    - name: Generate Oracle Environment Profile (oraenv)
      ansible.builtin.template:
        src: templates/oracle_profile.j2
        dest: "/home/{{ oracle_os_user }}/.bash_profile"
        owner: "{{ oracle_os_user }}"
        group: oinstall
        mode: '0644'
Use code with caution.
4. Execution Command
bash
ansible-playbook playbook.yml -i inventories/hosts -e "env=dev_exacc"
Use code with caution.

Interview Q&A Section
Q1: What happens if you reference vars_files: "other_var/{{ env }}.yml" but fail to pass the env variable during execution?
Answer: The playbook will fail immediately at initialization prior to execution because Ansible evaluates vars_files at the early parsing phase. To prevent this structural crash, implement a Jinja2 filter fallback default expression in your statement: [1]
yaml
vars_files:
  - "other_var/{{ env | default('dev_exacc') }}.yml"
Use code with caution.
Q2: Why use vars_files with dynamic variables over standard directory structures like group_vars/ or host_vars/?
Answer: While group_vars automatically binds properties directly onto implicit inventory host groups, using explicit vars_files pathways with a variable provides strict, declarative controls over software environments. It decouples properties entirely from inventory assignments, allowing developers on a dev branch to force-test variations of profiles across the identical target hardware simply by overriding a runtime flag. [1]
Q3: How does variable precedence operate when utilizing vars_files versus passing extra arguments with -e?
Answer: Command line variables passed with the -e (--extra-vars) flag carry the absolute highest precedence in Ansible. If a parameter named cpu_cores exists inside other_var/dev_exacc.yml with a value of 4, executing the playbook using: [1, 2]
bash
ansible-playbook playbook.yml -e "env=dev_exacc cpu_cores=8"
Use code with caution.
will override the internal file value, evaluating cpu_cores as 8. [1, 2]
Q4: How do you handle sensitive variables like Oracle passwords inside your other_var/{{env}}.yml files cleanly on a shared repository branch?
Answer: Secrets must never be kept plaintext inside any repository branch. To balance multi-environment configurations safely:
  1. Encrypt your individual variable files natively using Ansible Vault:
    bash
    ansible-vault encrypt other_var/dev_exacc.yml
    
    Use code with caution.
  2. Store the decrypt keys uniquely inside separated environment servers, calling the code file back while supplying authorization at runtime:
    bash
    ansible-playbook playbook.yml -e "env=dev_exacc" --ask-vault-pass

No comments:

Post a Comment