Ansible Data Structures - The Silent Architect Way

The Silent Architect Way

You install Ansible.
You automate the first system.
The gain is visible.
It feels liberating.

Projects. Inventories. Variables. Secrets.

New items pop up.
They're scattered around.
Then they're duplicated in different places.

Confusion.
Errors.
Bug hunts.

Re-architect your growing data - like a Silent Architect.

Variables Haunt You - Everywhere

You start it simple.

Variables thrive in tasks. Roles. Playbooks.
Defaults, vars files. Even in templates.

 name: DeadSwitch
 slogan: Fear the silence, Fear the Switch.

You just use it.

-  name:  Using a variable
   ansible.builtin.debug:
     msg:  "The variable is:  {{  slogan  }} ."

Then you realize you need structure.
You build lists.

 occupations:
  - Ghost Operator
  - Silent Architect
  - Digital Resistance Leader

The occupations variable is a list.
Comfortable.
You don't pick one element. You iterate the structure.

-  name:  Iterating a list
   ansible.builtin.debug:
     msg: The element is:  {{  item  }}.
   loop:  " {{  occupations  }} "

The silent echo comes:

ok: [localhost] => ( item=Ghost Operator) => The element is: Ghost Operator
ok: [localhost] => ( item=Silent Architect) => The element is: Silent Architect
ok: [localhost] => ( item=Digital Resistance Leader) => The element is: Digital Resistance Leader

With dictionaries you identify the values.
They have keys.

 user:
   deadswitch:
     name: DeadSwitch
     slogan: Fear the silence, Fear the Switch.
     occupation: Silent Architect

You can pick an element.

-  name:  Picking a dictionary element
   ansible.builtin.debug:
     msg:  "The user's occupation is:  {{  user.deadswitch.occupation  }} ."

Clean and dry.

Then Dark Ghosts possess the structure.
The Architect must tame them.

When The Structure Is Haunted - Embedded Data Structures

The lists were clean.
The dictionaries were sharp.

Then they merge.
They nest.
They twist inside each other.

 servers:
   web:
     hosts:
      - web1.ghostnet.local
      - web2.ghostnet.local
     vars:
       role: frontend
       ssl:  true
   db:
     hosts:
      - db1.ghostnet.local
     vars:
       role: backend
       replication: enabled

Now the structure is alive.
Keys hold lists. Lists hide keys.
Ghost Operators must know the paths.

-  name:  Access a nested dictionary
   ansible.builtin.debug:
     msg:  "DB role is:  {{  servers.db.vars.role  }} "
-  name:  Iterate hosts of the web group
   ansible.builtin.debug:
     msg:  "Web host:  {{  item  }} "
   loop:  " {{  servers.web.hosts  }} "

Haunted structures demand clarity.
Without discipline, you chase ghosts.

Walk The Path Of The Ghosts - Iterate The Structure

You tamed the Dark Ghosts.
The paths are clear.
Now you want to walk it.
A part of it. Or the entire system.

 users:
  -  name:  ghost1
     role: watcher
  -  name:  ghost2
     role: operator
  -  name:  ghost3
     role: architect

Iterating a list of dictionaries:

-  name:  Walk through each ghost
   ansible.builtin.debug:
     msg:  " {{  item.name  }}  takes the role:  {{  item.role  }} "
   loop:  " {{  users  }} "

But sometimes the dictionary itself must be walked:

 secrets:
   key1: shadow
   key2: fog
   key3: silence
-  name:  Dict to items
   ansible.builtin.debug:
     msg:  "Key:  {{  item.key  }}  | Value:  {{  item.value  }} "
   loop:  " {{  secrets | dict2items  }} "

When the map is too twisted, flatten it.
When the ghosts hide, reveal them.

Exorcism Tools - Filters And Transforms

Walking the paths is not enough.
Sometimes you reshape them.
Bend them.
Burn away the noise.

Filters are the exorcism tools.

 users:
  -  name:  ghost1
     role: watcher
  -  name:  ghost2
     role: operator
  -  name:  ghost3
     role: architect

Map reveals only what you need:

-  name:  Extract only the roles
   ansible.builtin.debug:
     msg:  "Role:  {{  item  }} "
   loop:  " {{  users | map(attribute='role') | list  }} "

It whispers:

ok: [localhost] => ( item=watcher) => Role: watcher
ok: [localhost] => ( item=operator) => Role: operator
ok: [localhost] => ( item=architect) => Role: architect

Select banishes what does not fit:

-  name:  Keep only operators
   ansible.builtin.debug:
     msg:  "Operator:  {{  item.name  }} "
   loop:  " {{  users | selectattr('role', 'equalto', 'operator') | list  }} "

When the maze grows darker, json_query cuts through.
(JMESPath is your blade.)

-  name:  Using json_query
   ansible.builtin.debug:
     msg:  "Architect:  {{  users | json_query('[?role==`architect`].name')  }} "

The echo is:

ok: [localhost] => Architect: [ 'ghost3']

Sometimes you want a clean string.

-  name:  Using json_query
   ansible.builtin.debug:
     msg:  "Architect:  {{  users | json_query('[?role==`architect`].name') | first  }} "

The answer comes in a form of a string:

ok: [localhost] => Architect: ghost3

The Ghost Operator does not drown in data.
He transforms it.
The Architect reshapes the chaos into silence.

Final Whisper

The Operator is you.
You write the data. Architect the structure.
Then you bend it.

Lists grow, Dictionaries bind, Filters cut.