2

I'm fairly new to ansible so maybe this is easy to do.

We have a large number of devices that have a consistent naming convention among different environments.

app0[01:25].dev.domain.com
app[01:25].qa.domain.com
app[01:25].uat.domain.com
app[01:25].prod.domain.com

Various devices inside those app servers have specific roles like web server, app server, api server, etc and it is the same across environment. So app05 is a web server in dev, qa, uat, uat, prod.

Right now I'm using group_vars and assigning specific server roles to the group names. But I would prefer not to have to set up group names for each environment(webdev, webqa, webuat, etc)

Is there a simple solution to this? I think roles would complicate things a bit but maybe that is the solution?

flyerhawk
  • 27
  • 3

2 Answers2

1

Q: "Set up group names for each environment(webdev, webqa, webuat, etc)"

A: Create the variable dynamically, e.g.

- hosts: all
  gather_facts: false
  vars:
    env:
      app05: web
  tasks:
    - set_fact:
        env_local: "{{ env[_host]|d('none') }}{{ _group }}"
      vars:
        _arr: "{{ inventory_hostname.split('.') }}"
        _host: "{{ _arr.0 }}"
        _group: "{{ _arr.1 }}"
    - debug:
        var: env_local

gives

ok: [app01.dev.domain.com] => 
  env_local: nonedev
ok: [app02.dev.domain.com] => 
  env_local: nonedev
ok: [app03.dev.domain.com] => 
  env_local: nonedev
ok: [app04.dev.domain.com] => 
  env_local: nonedev
ok: [app05.dev.domain.com] => 
  env_local: webdev

Use the module add_host and create the groups dynamically. For example, create the file

shell> cat add_host_to_groups.yml
- add_host:
    name: "{{ i }}"
    groups: "{{ item }}{{ (i.split('.')).1 }}"
  loop: "{{ _hosts }}"
  loop_control:
    loop_var: i

and include it in the loop of the assignment rules

- hosts: all
  gather_facts: false
  vars:
    env:
      web: app05
  tasks:
    - include_tasks: add_host_to_groups.yml
      loop: "{{ env.keys()|list }}"
      vars:
        _hosts: "{{ ansible_play_hosts_all|select('match', env[item] ~ '.*') }}"
      run_once: true
    - debug:
        var: groups
      run_once: true

gives

  groups:
    all:
    - app01.dev.domain.com
    - app02.dev.domain.com
    - app03.dev.domain.com
      ...
    - app25.prod.domain.com
    webdev:
    - app05.dev.domain.com
    webprod:
    - app05.prod.domain.com
    webqa:
    - app05.qa.domain.com
    webuat:
    - app05.uat.domain.com

Use the groups in consecutive plays, e.g.

- hosts: webqa
  gather_facts: false
  tasks:
    - debug:
        var: ansible_play_hosts_all

gives

  ansible_play_hosts_all:
  - app05.qa.domain.com

Fit the dictionary env to your needs.

Vladimir Botka
  • 5,138
  • 8
  • 20
  • OK. Gonna need to process this a bit but that looks awesome. I'll come back once I figured out what you are suggesting here. LOL. – flyerhawk Dec 30 '21 at 21:20
1

An inventory scheme that establishes a source of truth would be to query these groups from some system. Something more sophisticated than strings in the DNS name.

For example, netbox inventory database system has a device role that could fit. Not to pick on netbox, but it has an Ansible inventory plugin, and an open database model so is easy to talk about.


However, maybe a nice external database does not exist yet. Such a regular naming scheme can be generated. With that most recursive of Ansible inventory plugins, generator:

# inventory.yml
plugin: generator
hosts:
    name: "{{ application }}{{ number }}.{{ environment }}.example.com"
    parents:
      - name: "{{ application }}_{{ environment }}"
        parents:
          - name: "{{ application }}"
            vars:
              application: "{{ application }}"
          - name: "{{ environment }}"
            vars:
              environment: "{{ environment }}"
layers:
    application:
        - app
        - api
    environment:
        - dev
        - qa
        - uat
        - prod
    number:
        - "01"
        - "02"
        - "05"

Layer names are arbitrary. Given the "hosts" root and the "parents" notation, deeper indented names are groups that contain the outer names.

ansible-inventory -i inventory.yml --list will print hosts in Ansible's inventory JSON document. Partial output:

{
    "_meta": {
        "hostvars": {

           "app05.qa.example.com": {
                "application": "app",
                "environment": "qa"
            }
        }
    },

    "app_qa": {
        "hosts": [
            "app01.qa.example.com",
            "app02.qa.example.com",
            "app05.qa.example.com"
        ]
    },

    "app": {
        "children": [
            "app_dev",
            "app_prod",
            "app_qa",
            "app_uat"
        ]
    },

    "qa": {
        "children": [
            "api_qa",
            "app_qa"
        ]
    },


}

And continuing on for other combinations.

Notice it made:

  • "application" groups
  • "environment" groups
  • "application environment" combination groups
  • host names conforming to the DNS names pattern
  • vars containing the "application" and "environment" for each host

Limitations of this plugin include:

Always doing the cartesian product of the layer combinations. Cannot have more or less of a certain group, nor start the numbering schemes on different values.

Not having compact host ranges. Neither [01:25] syntax nor the range() function works. Consider requesting that by filing an issue. As a workaround, dozens of numbers in the config file is functional.

John Mahowald
  • 32,050
  • 2
  • 19
  • 34