« Ansible - Infrastructure-as-a-Code » : différence entre les versions

De Marijan Stajic | Wiki
Aller à la navigation Aller à la recherche
m (→‎Variables : Precedence)
m (Marijan a déplacé la page Ansible - IaC vers Ansible - Infrastructure-as-a-Code)
 
(Aucune différence)

Dernière version du 30 avril 2025 à 22:16

Introduction

Ansible Banner.png

Ansible is an open-source automation and orchestration tool based on YAML and SSH, used to automate repetitive tasks in infrastructure management, such as provisioning, configuration management, continuous delivery, application deployment, and security compliance.

Developing scripts for these tasks requires time, coding skills, and ongoing maintenance. With Ansible, automation becomes simple and efficient

Configuration Files

Ansible provides a default configuration file, usually located at /etc/ansible/ansible.cfg. This file contains various settings such as the inventory file location, SSH connection options, privilege escalation rules, and more. Depending on the project you're working on, you can define a separate configuration file in a different directory with custom settings. This is one way to override specific parameters in Ansible without changing the global config.

In addition to the configuration file, you can create a playbook, written in YAML. A playbook describes what tasks to run on which hosts. It typically defines target hosts, tasks to execute, roles to apply, variables, handlers, and other automation logic. Playbook is explain more in an another section.

Be careful, when running a playbook, you should explicitly specify which configuration file to use by setting it through an environment variable, or directly in the Playbook file :

marijan$ ANSIBLE_CONFIG=/path/to/ansible.cfg ansible-playbook playbook.yml

Here is the priority order Ansible uses to determine which configuration file to load :

  1. Environment variable, you can specify the configuration file using the ANSIBLE_CONFIG environment variable, either when running the command or in your environment setup.
  2. Configuration file in the current directory, if there’s an ansible.cfg file in the same directory where the playbook is being run, Ansible will use it (as long as no environment variable overrides it).
  3. Default configuration file, if neither of the above is set, Ansible will fall back to the default configuration file located at /etc/ansible/ansible.cfg.

For example, if you have a playbook that currently relies on the default configuration file, but you want to change just one parameter, you should first check the correct name of that parameter in the Ansible documentation or by using command. This is important because the name you see in the configuration file might not always match the name required for environment variables.

To see the full list of available parameters via the command line, run:

marijan$ ansible-config list

Once you've identified the correct variable name, you can use the export command (for a Linux shell) to set it as an environment variable :

marijan$ export ANSIBLE_INVENTORY=/path/to/your/inventory

This lets you override specific settings without modifying the entire config file. If you only want to change a parameter temporarily, for a single run, you can also specify it directly in the command using the environment variable.

You can check the value of a specific parameter and see where it was defined (e.g., from an environment variable, a config file, etc.) by running the following command :

marijan$ ansible-config dump | grep "parameter"

Finally, to see which configuration file Ansible is currently using, run :

marijan$ ansible-config view"

Inventory

To connect to different hosts in your infrastructure and execute Playbooks, Ansible uses SSH for Linux systems and PowerShell Remoting for Windows systems. Ansible is an agentless tool, which means it does not require any agent to be installed on the remote machines. It operates through standard protocols like SSH and PowerShell Remoting.

Information about the target systems is stored in an inventory file, which is located at /etc/ansible/hosts by default.

Here is an example of a sample Ansible inventory file:

marijan$ cat inventory.ini

web  ansible_host=server1.company.com ansible_connection=ssh   ansible_ssh_pass=P@ssw0rd ansible_port=4115
db   ansible_host=server2.company.com ansible_connection=winrm ansible_user=admin
mail ansible_host=server3.company.com ansible_connection=ssh   ansible_ssh_pass=P@ssw0rd
dev  ansible_host=server4.company.com ansible_connection=winrm ansible_ssh_pass=P@ssw0rd

In this example :

  • Each line defines a host alias (e.g., web, db, mail, dev) along with its connection parameters ;
  • ansible_host specifies the FQDN or IP address of the target machine ;
  • ansible_connection defines the protocol to use (e.g., ssh for Linux, winrm for Windows) ;
  • Optional variables like ansible_ssh_pass, ansible_port, and ansible_user are used to customise the connection.

For a production environment, it is recommended to use certificate-based authentication or SSH key-based, passwordless authentication.

To run a Playbook locally, you can set the following line in the host configuration file :

marijan$ cat inventory.ini

localhost ansible_host=localhost

Format

Depending on your project, it exists two kind of Inventory format :

  • INI : simplest and most straightforward format :
marijan$ cat inventory.ini

[web]
server1.company.com

[db]
server2.company.com ansible_user=admin ansible_connection=winrm
  • YAML : more structured and flexible format than the INIT format :
marijan$ cat inventory.yml

all:
  hosts:
    server1.company.com:
      ansible_connection: ssh
    server2.company.com:
      ansible_connection: winrm
      ansible_user: admin
  children:
    web:
      hosts:
        server1.company.com:
    db:
      hosts:
        server2.company.com:

Group and Parent-Child Relationships

No matter which inventory format you use INI or YAML defining groups allows you to target specific sets of hosts when running a Playbook. For example :

marijan$ cat inventory.ini

web1  ansible_host=server1.company.com ansible_connection=ssh   ansible_ssh_pass=P@ssw0rd ansible_port=4115
web2  ansible_host=server2.company.com ansible_connection=winrm ansible_user=admin
db1 ansible_host=server3.company.com ansible_connection=ssh   ansible_ssh_pass=P@ssw0rd
db2  ansible_host=server4.company.com ansible_connection=winrm ansible_ssh_pass=P@ssw0rd

[web]
web1, web2

[db]
db1, db2

To run a Playbook only on the db group :

marijan$ ansible-playbook playbook.yml -i inventory.yaml -l db

This command targets only the hosts in the db group, even though other hosts are listed in the inventory.

You can also group multiple groups under a parent group using the :children keyword. This is useful when you want to run a Playbook on a broader category of machines made up of smaller, related groups.

marijan$ cat inventory.ini

web1  ansible_host=server1.company.com ansible_connection=ssh   ansible_ssh_pass=P@ssw0rd ansible_port=4115
web2  ansible_host=server2.company.com ansible_connection=winrm ansible_user=admin
db1 ansible_host=server3.company.com ansible_connection=ssh   ansible_ssh_pass=P@ssw0rd
db2  ansible_host=server4.company.com ansible_connection=winrm ansible_ssh_pass=P@ssw0rd

[web]
web1, web2

[db]
db1, db2

[app_servers:children]
web, deb

Here is the same example in .YAML format :

marijan$ cat inventory.yml

all:
  children:
    web:
      hosts:
        web1:
          ansible_host: server1.company.com
          ansible_connection: ssh
          ansible_ssh_pass: P@ssw0rd
        web2:
          ansible_host: server2.company.com
          ansible_connection: winrm
          ansible_user: admin

    db:
      hosts:
        db1:
          ansible_host: server3.company.com
          ansible_connection: ssh
          ansible_ssh_pass: P@ssw0rd
        db2:
          ansible_host: server4.company.com
          ansible_connection: winrm
          ansible_ssh_pass: P@ssw0rd

    app_servers:
      children:
        web:
        db:

Variables

Just like in any other programming language, variables are used in Ansible to store and reference values.

In an Ansible Playbook, you can define variables using the vars: directive or by storing them in separate variable files. For now, let's focus on defining variables directly inside the vars section of a Playbook :

marijan$ cat Playbook.yml

- name: Add DNS server to resolv.conf
  hosts: localhost
  vars:
    dns_server: 10.1.250.10
  tasks:
    - lineinfile:
        path: /etc/resolv.conf
        line: 'nameserver 10.1.250.10'

Then, to use the defined variable, we call it using Jinja2 templating syntax, like this:

marijan$ cat Playbook.yml

- name: Add DNS server to resolv.conf
  hosts: localhost
  vars:
    dns_server: 10.1.250.10
  tasks:
    - lineinfile:
        path: /etc/resolv.conf
        line: 'nameserver {{ dns_server }}'

Types

Ansible supports different types of variables. Here are the most commonly used ones :

  • String : A string is a sequence of characters enclosed in quotes (single or double) :
username: "admin"
  • Number : Numbers can be integers or floating-point values :
max_connections: 100
  • Boolean : Booleans can hold either true or false, and are often used in conditionals :
debug_mode: true # True, 'true', 't', 'yes', 'on'
  • List : A list is an ordered collection of values (can be strings, numbers, or even other lists/dictionaries) :
packages:
   - nginx
   - postgresql
   - git
  • Dictionnary : A dictionary holds a collection of key-value pairs, where each key maps to a specific value :
user:
   name: "admin"
   password: "secret"

To reference an item from a list, use square brackets with the index (starting at 0) :

msg: "The first package is {{ packages[0] }}"

To access a value inside a dictionary, use either dot notation or bracket notation. Here's how you can access nested keys :

msg: "Username: {{ user.name }},Password: {{ user.password }}"

Precedence

Ansible operates based on a specific order of variable precedence. For example, consider the following inventory.ini file:

marijan$ cat inventory.ini

db1  ansible_host=server1.company.com dns_server=10.5.5.4
db2  ansible_host=server2.company.com
db3 ansible_host=server3.company.com

[db_servers]
db1
db2
db3

[db_servers:vars]
dns_server=10.5.5.3

In this example, the dns_server variable defined under [db_servers:vars] (i.e., 10.5.5.3) applies to all hosts in the db_servers group, except for db1. That’s because db1 has a dns_server value (10.5.5.4) defined directly in its host line, which takes precedence over the group-level variable.

Now, imagine you define the dns_server variable again in a playbook, that value would override both the group-level and host-level values.

marijan$ cat playbook.yml

- hosts: db_servers
  vars:
    dns_server: 10.5.5.6
  tasks:
    - name: Show DNS server in use
      debug:
        msg: "Using DNS server {{ dns_server }}"

Finally, if you pass the dns_server value using the CLI (for example with the -e flag), it will override all previously defined values. This is because CLI variables have the highest precedence.

marijan$ ANSIBLE_EXTRA_VARS="dns_server=1.1.1.1"
marijan$ ansible-playbook -i inventory.ini playbook.yml -e "$ANSIBLE_EXTRA_VARS"

There are many levels of variable precedence in Ansible, and you can find a full list in the Ansible documentation.