Ansible group_vars not loading based on environment / host list - ansible

I have a playbook like so:
- hosts: "{{env}}"
name: "REDIS Playbook"
sudo: no
vars:
product: redis
roles:
- redis
And I call it with: ansible-playbook pb_redis.yml -i inventory/redis -e env=qa -v
I have a directory structure like:
.
├── group_vars
│   ├── qa
│   │   ├── common
│ │ | └── redis.yml
│   │   ├── products
│ │ | └── abc-1.yml
│ │ | └── xyz-2.yml
│   ├── test
│ │ ├── common
│ │ | └── redis.yml
│ │ ├── products
│ │ | └── abc-1.yml
│ │ | └── xyz-2.yml
├── inventory
└── roles
└── redis
├── files
├── handlers
├── meta
├── tasks
├── templates
└── vars
And I have an inventory like:
[qa:children]
qa_redis
[qa_redis]
mybox.1.space
mybox.2.space
mybox.3.space
My Issue: When I run ansible-playbook pb_redis.yml -i inventory/redis -e env=qa -v, I'm still picking up group_vars defined in the ../test/common/redis.yml instead of ../qa/common/redis.yml -- am I misunderstanding how this should work? The correct hosts get picked up from the inventory file, but not the correct group_vars. Should I place the redis.yml under ../qa/products/ instead?

Inventory (host and group) variables in Ansible are bound to host. Group variables exist for convenience.
If a host is in multiple groups at the same time, all group variables are applied to that host.
If different groups have same variables, they overwrite each other during inventory load process.
So if you have mybox.1.space in groups qa and test, variables from groups qa and test are applied to this host.
Usually you want to use separate inventories to work with different deploy environments. And groups are used to separate different logical units inside inventory.

Related

Ansible not recognising variables not found under host_vars

Ι have the following inventory
[foo]
foosrv ansible_host=11.22.33.44
[bar]
barsrv ansible_host=44.11.22.33
[zoo]
zoosrv ansible_host=21.21.21.21
and the following file structure
.
├── ansible.cfg
├── files
│ └── aws.yaml
├── host_vars
│ ├── foosrv
│ │ ├── templates
│ │ │ └── repoConf.yaml.j2
│ │ └── vault
│ └── barsrv
│ ├── templates
│ │ └── repoConf.yaml.j2
│ └── vault
├── inventory
├── site.yaml
├── templates
│ ├── amend.py.j2
│ └── config.j2
└── vars
├── ansible_vars.yaml
└── vault
My problem is that any variables under ./vars/vault are not recognized by ansible.
Any hints about what might be the issue here?
Ansible doesn't automatically load variables from a vars directory.
You have several options for loading variables:
Ansible will load variables from a file (or directory) in host_vars matching the inventory hostname.
Ansible will load variables from a file (or directory) in group_vars matching the names of groups of which a host is a member.
You can use the include_vars task to explicitly load variables from a file.
If you want to load the variables for all hosts automatically, then place them in group_vars/all.yaml (or a subdirectory of group_vars/all).
Ansible will automatically detect (and decrypt, if you've provided a password) files that are vault encrypted.

Ansible in multi environments

I was wondering if anyone could advise on how I can manage multi environment variables using ansible. Imagine i have development and staging. I have the following structure:
.
├── inventories
│   ├── development
│   │   ├── group_vars
│   │   │   └── all.yml
│   │   └── hosts
│   └── staging
│   ├─── group_vars
│ │ └── all.yml
│ │ └── hosts
├── roles
└── test.yml
In each all.yml file, i have one variable like this:
vars:
name:"development"
I was wondering how is the best approach to load the values depending on a parameter to set for example on command line (-i env=development).
In test.yml, I have this:
- name: test playbook
hosts: localhost
connection: local
tasks:
- name:
debug:
var: name
When I execute, I get this:
ansible-playbook -i inventories/development/ test.yml -vvv
....
ok: [localhost] => {
"name": "VARIABLE IS NOT DEFINED!: 'name' is undefined"
}
I'm kind of new to ansible and I've found thousands of different ways and I was wondering what is the best approach? Because I'm kind of lost :( :(
Regards,
We have something similar to your needs on our project, I guess.
Organized in below maner:
.
group_vars folder
├── all.yml # variables common for all envs (creds for example)
├── development.yml # variables for development env
└── staging.yml # variables for staging env
|
invenory_file.yml # file which stoers your managed hosts list devided by groups
├── [development]
│ ├── dev.host1.name # development host#1 with vars applied from development.yml
│ ├── dev.host2.name # development host#2 with vars applied from development.yml
│ └── dev.host3.name # development host#3 with vars applied from development.yml
├── [staging]
│ ├── staging.host1.name # staging host#1 with vars applied from staging.yml
│ ├── staging.host2.name # staging host#2 with vars applied from staging.yml
│ └── staging.host3.name # staging host#3 with vars applied from staging.yml
|
roles
Main idea that you have separate files with vars for different envs, and you have your hosts organized in environments groups in inventory file. So variables from files in group_vars will applied to correct hosts grouped in Inventory.
Here is usuful link to check on how Ansible groups are organized:
https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html

Ansible ask for password to ansible vault from other group vars

I have problem with ansible.
I have couple of group_vars folders and in this folders there is files encrypted by ansible-vault with difference passwords between prod and test:
├── group_vars
│   ├── app1_prod
│   │   ├── application.yml <- Ancryptes by Ansible Vault prod pass
│   │   └── service.yml
│   ├── app1_test
│   │   ├── application.yml <- Ancryptes by Ansible Vault test pass
│   │   └── service.yml
│   ├── app2_prod
│   │   ├── application.yml <- Ancryptes by Ansible Vault prod pass
│   │   └── service.yml
│   └── app2_test
│   ├── application.yml <- Ancryptes by Ansible Vault test pass
│   └── service.yml
And my inventory file looks like:
[test_hosts]
test_host1
test_host2
[prod_hosts]
prod_host1
prod_host2
[app1_test:children]
test_hosts
[app2_test:children]
test_hosts
[app1_prod:children]
prod_hosts
[app2_prod:children]
prod_hosts
When I running playbook command:
ansible-playbook app1_playbook.yml -i ./inventory/hosts -l app1_test -u ssh_user -k --vault-password-file path_to_vault_key
I get error that saying the vault password is wrong for file and pointing for file in prod and from other group:
Decryption failed on ansible/group_vars/app1_prod/application.yml
I don't know how to fix this.
Personally, I think your inventory structure is a Bad Idea. I do not condone having PROD and TEST servers in the same inventory, and I see no good reason for it.
I would restructure your system like this:
├── prod
│ ├── ansible.cfg
│ ├── group_vars
│ │ ├── app1
│ │ │ ├── application.yml <- Ancryptes by Ansible Vault prod pass
│ │ │ └── service.yml
│ │ ├── app2
│ │ │ ├── application.yml <- Ancryptes by Ansible Vault prod pass
│ │ │ └── service.yml
├── test
│ ├── ansible.cfg
│ ├── group_vars
│ │ ├── app1
│ │ │ ├── application.yml <- Ancryptes by Ansible Vault prod pass
│ │ │ └── service.yml
│ │ ├── app2
│ │ │ ├── application.yml <- Ancryptes by Ansible Vault prod pass
│ │ │ └── service.yml
And, of course, there would be two host files:
PROD:
[hosts]
prod_host1
prod_host2
[app1:children]
hosts
[app2:children]
hosts
TEST:
[hosts]
test_host1
test_host2
[app1:children]
hosts
[app2:children]
hosts
Have an ansible.cfg file in each inventory directory with the lines:
inventory = .
vault_password_file = /path/to/vault_password_file
remote_user = ssh_user
ask_pass = True
(Best if you just copy /etc/ansible/ansible.cfg to the inventory directory and change what you need to change.)
Once you have that setup, you go into the prod or test directory, and execute the playbook from there. Of course, you will need to specify the path to the playbooks:
cd prod
ansible-playbook /path/to/playbooks/app_playbook.yml
cd test
ansible-playbook /path/to/playbooks/app_playbook.yml
Trust me, life is much easier with inventory separation.
Good luck!

select group_var variable file in top level playbook

I have defined all my variables in group_vars/all/vars_file.yml and my playbook is as below:
---
# Top level play site.yml
- hosts: webclient
roles:
- common
- nginx
- nvm
- deploy_web_client
- hosts: appserver
roles:
- common
- gradle
- tomcat
- deploy_sb_war
Now I have 3 environments dev / staging / production. Depending upon the environment i used to change the vars_file.yml under group_vars and then run the ansible-play.
Is there any way I can keep 3 files like "group_vars/dev" , "group_vars/staging", "group_vars/production" and specify it in my main site.yml
I have 3 inventory files as below, and depending upon the environment during ansible-play i specify the inventory file name
[webclient]
10.20.30.40
[appserver]
10.20.30.41
Instead of using inventory files saved in a single directory, use inventory files in separate directories and put group_vars inside each of them.
.
├── dev
│   ├── group_vars
│   │   └── all
│   │   └── vars_file.yml
│   └── inventory
├── production
│   ├── group_vars
│   │   └── all
│   │   └── vars_file.yml
│   └── inventory
└── staging
├── group_vars
│   └── all
│   └── vars_file.yml
└── inventory
Then point to the directory in the ansible-playbook call:
ansible-playbook -i dev <the_rest>

How to share group_vars between different inventories in Ansible?

The Ansible best practices documentation recommends to separate inventories:
inventories/
production/
hosts.ini # inventory file for production servers
group_vars/
group1 # here we assign variables to particular groups
group2 # ""
host_vars/
hostname1 # if systems need specific variables, put them here
hostname2 # ""
staging/
hosts.ini # inventory file for staging environment
group_vars/
group1 # here we assign variables to particular groups
group2 # ""
host_vars/
stagehost1 # if systems need specific variables, put them here
stagehost2 # ""
My staging and production environments are structured in the same way. I have in both environments the same groups. And it turns out that I have also the same group_vars for the same groups. This means redundancy I would like to wipe out.
Is there a way to share some group_vars between different inventories?
As a work-around I started to put shared group_vars into the roles.
my_var:
my_group:
- { var1: 1, var2: 2 }
This makes it possible to iterate over some vars by intersecting the groups of a host with the defined var:
with_items: "{{group_names | intersect(my_var.keys())}}"
But this is a bit complicate to understand and I think roles should not know anything about groups.
I would like to separate most of the inventories but share some of the group_vars in an easy to understand way. Is it possible to merge global group_vars with inventory specific group_vars?
I scrapped the idea of following Ansible's recommendation. Now one year later, I am convinced that Ansible's recommendation is not useful for my requirements. Instead I think it is important to share as much as possible among different stages.
Now I put all inventories in the same directory:
production.ini
reference.ini
And I take care that each inventory defines a group including all hosts with the name of the stage.
The file production.ini has the group production:
[production:children]
all_production_hosts
And the file reference.ini has the group reference:
[reference:children]
all_reference_hosts
I have just one group_vars directory in which I define a file for every staging group:
group_vars/production.yml
group_vars/reference.yml
And each file defines a stage variable. The file production.yml defines this:
---
stage: production
And the file reference.yml defines that:
---
stage: reference
This makes it possible to share everything else between production and reference. But the hosts are completely different. By using the right inventory the playbook runs either on production or on reference hosts:
ansible-playbook -i production.ini site.yml
ansible-playbook -i reference.ini site.yml
If it is necessary for the site.yml or the roles to behave slightly different in the production and reference environment, they can use conditions using the stage variable. But I try to avoid even that. Because it is better to move all differences into equivalent definitions in the staging files production.yml and reference.yml.
For example, if the group_vars/all.yml defines some users:
users:
- alice
- bob
- mallory
And I want to create the users in both environments, but I want to exclude mallory from the production environment, I can define a new group called effective_users. In the reference.yml it is identical to the users list:
effective_users: >-
{{ users }}
But in the production.yml I can exclude mallory:
effective_users: >-
{{ users | difference(['mallory']) }}
The playbook or the roles do not need to distinguish between the two stages, they can simply use the group effective_users. The group contains automatically the right list of users simply by selecting the inventory.
The simple option here (and what we do) is simply symlink generic group vars files around.
For instance we might have a generic role for something like NGINX and then a few concrete use cases for that role. In this case we create a group vars file that uses the NGINX role for each concrete use case and then simply symlink those group vars files into the appropriate folders.
Our project folder structure then might look something like this (drastically simplified):
.
├── inventories
│   ├── bar-dev
│   │   ├── group_vars
│   │   │   ├── bar.yml -> ../../shared/bar.yml
│   │   │   └── dev.yml -> ../../shared/dev.yml
│   │   └── inventory
│   ├── bar-prod
│   │   ├── group_vars
│   │   │   ├── bar.yml -> ../../shared/bar.yml
│   │   │   └── prod.yml -> ../../shared/prod.yml
│   │   └── inventory
│   ├── bar-test
│   │   ├── group_vars
│   │   │   ├── bar.yml -> ../../shared/bar.yml
│   │   │   └── test.yml -> ../../shared/test.yml
│   │   └── inventory
│   ├── foo-dev
│   │   ├── group_vars
│   │   │   ├── dev.yml -> ../../shared/dev.yml
│   │   │   └── foo.yml -> ../../shared/foo.yml
│   │   └── inventory
│   ├── foo-prod
│   │   ├── group_vars
│   │   │   ├── foo.yml -> ../../shared/foo.yml
│   │   │   └── prod.yml -> ../../shared/prod.yml
│   │   └── inventory
│   ├── foo-test
│   │   ├── group_vars
│   │   │   ├── foo.yml -> ../../shared/foo.yml
│   │   │   └── test.yml -> ../../shared/test.yml
│   │   └── inventory
│   └── shared
│   ├── bar.yml
│   ├── dev.yml
│   ├── foo.yml
│   ├── prod.yml
│   └── test.yml
└── roles
└── nginx
├── defaults
│   └── main.yml
├── meta
│   └── main.yml
├── tasks
│   └── main.yml
└── templates
└── main.yml
Now our inventory files can have the hosts use these shared group vars simply by putting the hosts in the correct groups.
You can place group_vars in playbook directory as well. More info.
Ansible will pick them up for all inventories.

Resources