I have a multi environment & multi inventories setup within ansible (2.7.9).
With one of the inventories, I am wanting to set a global variable to be inherited by all the hosts within the inventory. For this purpose I added the variable into that specific inventory (inventory/production/prodinv):
[all:vars]
myvar="True"
And it works fine if I ran ansible against that specific inventory (inventory/production/prodinv). However, if I run ansible against the inventory directory (eg inventory/production) , I noticed that the variable is inherited on all the hosts across all the inventories - which isn't ideal because I only want the hosts within firstenv inventory to have the var defined.
Currently group_vars and host_vars are a symlink (for all the inventories) against a "shared" root group_vars and host_vars.
To add more clarity to my question, below is the structure of my ansible:
.
├── ansible.cfg
├── playbooks/
├── roles/
├── inventory/
│ │
│ ├── group_vars/
| |
| ├── host_vars/
| |
│ ├── tnd/
│ │ ├── group_vars/ -> ../group_vars
│ | ├── host vars/ -> ../host_vars
│ │ └── devinv
│ │
│ ├── production/
│ │ ├── group_vars/ -> ../group_vars
│ | ├── host vars/ -> ../host_vars
│ │ └── prodinv
│
└── . .
I'm not sure how / where to define this var that should apply to all hosts/groups within a particular inventory, without running into the same issue. Ideas?
Thanks,
J
I think your problem is two-fold.
Ansible applies the group_vars of a directory to all files and subdirectories within the specified inventory directory. So, inventory/production/group_vars will get applied to everything within inventory/production. This just gets masked when you explicitly limit your inventory further while running, like you did (-i inventory/production/prodinv).
This means that you need to put the group_vars only being applied to prodinv in their own directory and not in the inventory/production directory. For example, inventory/production/prodinv/group_vars.
Your symlinks are set up in a way that if you run against inventory, you're going to have the same group_vars applied to all your inventories. You're not hitting this in your example, but you'll likely hit it in the future.
Related
I am experimenting with variable overriding in Ansible. To do so, I have created the below-depicted directory structure. Note that under inventories, I have created two separate sites (1 & 2)
Also, note that I have added group_vars/host_vars at two different levels; below inventories and each site.
.
├── ansible.cfg
├── inventories
│ ├── group_vars
│ │ └── all.yml
│ ├── host_vars
│ │ └── target2.yml
│ ├── site1
│ │ ├── group_vars
│ │ │ └── all.yml
│ │ ├── host_vars
│ │ │ └── target1.yml
│ │ └── hosts.yml
│ └── site2
│ ├── group_vars
│ │ └── all.yml
│ ├── host_vars
│ │ └── target2.yml
│ └── hosts.yml
├── modules
├── playbooks
│ └── playbook1
│ ├── group_vars
│ │ └── all.yml
│ └── host_vars
└── roles
I would like to be able to store default variables for groups/hosts at "inventories" level and override them when/if necessary at site/group/host level using directories (not the hosts.yml), but I am unable to do so.
If I test the inventory by targeting the base "inventories" directory, I can see that group_vars/host_var folders under sites are ignored:
ansible-inventory --vars --graph -i inventories/
#all:
|--#site1:
| |--target1
| | |--{scope = inventories/site1/hosts.yml}
| |--target2
| | |--{scope = inventories/host_vars/target2.yml}
|--#site2:
| |--target2
| | |--{scope = inventories/host_vars/target2.yml}
|--#ungrouped:
|--{scope = inventories/group_vars/all.yml}
But if I target a specific site, the underlying group_vars/host_var folder are used, but of course the one at base "inventory" are ignored:
ansible-inventory --vars --graph -i inventories/site1
#all:
|--#site1:
| |--target1
| | |--{scope = inventories/site1/host_vars/target1.yml}
| |--target2
| | |--{scope = inventories/site1/group_vars/all.yml}
|--#ungrouped:
|--{scope = inventories/site1/group_vars/all.yml}
ansible-inventory --vars --graph -i inventories/site2
#all:
|--#site2:
| |--target2
| | |--{scope = inventories/site2/host_vars/target2.yml}
| |--{scope = inventories/site2/group_vars/all.yml}
|--#ungrouped:
Is it possible to instruct ansible to look for group_vars/host_var folders in the entire directory structure?
Thanks!
According your description and example your sets site1 and site2 are already subsets of set all. You made the observation and described it in your question
If I target a specific site, the underlying group_vars/host_var folder are used, but of course the one at base "inventory" are ignored
also the used command gives a hint
ansible-inventory --vars --graph -i inventories/site1 # or 2
since with this you'll set the root of a tree structure or graph to a sub tree or part of a graph.
Is it possible to instruct Ansible to look for group_vars/host_var folders in the entire directory structure?
No, since there will be not other directory outside of the defined subset, sub tree or graph part.
but of course the one at base "inventory" are ignored
In other words, the base "inventories" doesn't exists (anymore) since you've set the base to "inventories/site1" or 2.
An other approach could be to have one inventory for each site.
I try to push specific conf with group_vars, but it only make push for one instance aa.yml and I don't have the push for bb.yml inventory. I already used group_vars and works before, but not with conf ansible
- name: Push conf
uri:
url: "https://xxx{{ instance_id }}"
method: POST
status_code: [201]
headers:
Content-Type: application/json
body_format: json
body: "{\"server\":{{ server }},\"labels\":{{{ site }}},\"name\":\"{{ instance.value.name }}"
return_content: true
vars:
instance: "{{ item }}"
loop: "{{ instances }}"
inventory/host/group_vars/aa/aa.yml
site: "\"aa\""
instance_id: "06a56590"
server: "[\"server1\"]"
inventory/host/group_vars/bb/bb.yml
site: "\"bb\""
instance_id: "bcc37660"
server: "[\"server2\"]"
inventory/host/000_hosts
[host]
server1
server2
The command:
ansible-playbook task.yml -i inventory/host/000_hosts --extra-vars "target=host"
Supplying with an answer:
group_vars/XXX directories typically refers to groups defined in your inventory, and they contain variables only available for that group. In your case you created directories for the groups aa and bb, these groups does not exists in your inventory, meaning when you call your playbook referring to your hosts (- hosts: host), ansible will look for group variables related to that group. Which is this case does not exists.
As you will see in my suggestion below; by using the keyword children in your inventory, you are basically saying: The hosts defined in the group aa/bb is part/children of the group host (the parent), and the variables follows. (inheriting-variable-values-group-variables-for-groups-of-groups)
Changing your inventory to the following, should solve the problem:
inventory/host/hosts
[aa]
server1
[bb]
server2
[host:children]
aa
bb
You could also change your directory structure to something like:
inventory/
├── group_vars
│ ├── aa
│ │ └── aa.yml
│ └── bb
│ └── bb.yml
└── hosts
Edit:
However, if I'm not mistaken: your hosts directory (in inventory/hosts) is typically used to identify your environment like:
Multistage environment Ansible
.
├── ansible.cfg
├── environments/ # Parent directory for our environment-specific directories
│ │
│ ├── dev/ # Contains all files specific to the dev environment
│ │ ├── group_vars/ # dev specific group_vars files
│ │ │ ├── all
│ │ │ ├── db
│ │ │ └── web
│ │ └── hosts # Contains only the hosts in the dev environment
│ │
│ ├── prod/ # Contains all files specific to the prod environment
│ │ ├── group_vars/ # prod specific group_vars files
│ │ │ ├── all
│ │ │ ├── db
│ │ │ └── web
│ │ └── hosts # Contains only the hosts in the prod environment
│ │
│ └── stage/ # Contains all files specific to the stage environment
│ ├── group_vars/ # stage specific group_vars files
│ │ ├── all
│ │ ├── db
│ │ └── web
│ └── hosts # Contains only the hosts in the stage environment
│
├── playbook.yml
│
└── . . .
Take a look at organizing-host-and-group-variables
I'm building an AMI with packer and providing it with Ansible. The AMI needs to be build with different variables depending on the environment.Therefore I used the below structure based on this article:
├── ansible.cfg
├── bamboo-server-dev.yml
├── environments
├── group_vars
│ ├── bamboo_packer_dev.yml
│ └── bamboo_packer_prod.yml
└── roles/
├── bamboo-server/
│ └── vars/
│ ├── bamboo-dev.yml
│ ├── bamboo-prod.yml
│ └── main.yml
└── ubuntu-server/
My environment file is where I define my inventory and looks like this:
[bamboo_packer_dev]
localhost
[bamboo_packer_prod]
localhost
The group_vars/bamboo_packer_dev.yml looks like the below:
---
# Setup specific variables per environment
environment: "dev"
The playbook bamboo-server-dev.yml looks like this:
---
- hosts: bamboo_packer_dev
become: true
roles:
- ubuntu-server
- bamboo-server
And finally the roles/bamboo-server/vars/main.yml looks like this:
---
# Provide variables according to the environment
- name: provide variables
include_vars:
file: "bamboo-{{ environment }}.yml"
But when I run the playbook I get the following message:
ERROR! failed to combine variables, expected dicts but got a 'dict' and a 'AnsibleSequence':
{}
[{"name": "provide variables", "include_vars": {"file": "bamboo-{{ environment }}.yml"}}]
It seems the "environment" variable is not getting picked up. And I cannot figure it why. Can someone please suggest what might be wrong with my playbook? Or abetter way to achieve this? My Ansible version is 2.9.1 running on Mojave.
Your vars file cannot include ansible tasks, as you have done. Files in vars must be -- as ansible very clearly informed you -- dict and not list, since the keys of the vars file becomes variable names, and [] is not a variable name
Perhaps you meant to put that content into roles/bamboo-server/tasks/main.yml, which will allow you to have include_vars: as an action
I have my directory structure as this
└── digitalocean
├── README.md
├── play.yml
└── roles
├── bootstrap_server
│ └── tasks
│ └── main.yml
├── create_new_user
│ └── tasks
│ └── main.yml
├── update
│ └── tasks
│ └── main.yml
└── vimserver
├── files
│ └── vimrc_server
└── tasks
└── main.yml
When I am creating a user under the role create_new_user, I was hard coding the user name as
---
- name: Creating a user named username on the specified web server.
user:
name: username
state: present
shell: /bin/bash
groups: admin
generate_ssh_key: yes
ssh_key_bits: 2048
ssh_key_file: .ssh/id_rsa
- name: Copy .ssh/id_rsa from host box to the remote box for user username
become: true
copy:
src: ~/.ssh/id_rsa.pub
dest: /home/usernmame/.ssh/authorized_keys
mode: 0600
owner: username
group: username
One way of solving this may be to create a var/main.yml and put the username there. But I wanted something through which I can specify the username at play.yml level. As I am also using the username in the role vimrcserver.
I am calling the roles using play.yml
---
- hosts: testdroplets
roles:
- update
- bootstrap_server
- create_new_user
- vimserver
Would a template work here in this case? Couldn't find much from these SO questions
I got it working by doing a
---
- hosts: testdroplets
roles:
- update
- bootstrap_server
- role: create_new_user
username: username
- role: vimserver
username: username
on play.yml
Although would love to see a different approach then this
Docs: http://docs.ansible.com/ansible/playbooks_roles.html#roles
EDIT
I finally settled with a directory structure like
$ tree
.
├── README.md
├── ansible.cfg
├── play.yml
└── roles
├── bootstrap_server
│ └── tasks
│ └── main.yml
├── create_new_user
│ ├── defaults
│ │ └── main.yml
│ └── tasks
│ └── main.yml
├── update
│ └── tasks
│ └── main.yml
└── vimserver
├── defaults
│ └── main.yml
├── files
│ └── vimrc_server
└── tasks
└── main.yml
Where I am creating a defaults/main.yml file inside the roles where I need the usage of {{username}}
If someone is interested in the code,
https://github.com/tasdikrahman/ansible-bootstrap-server
You should be able to put username in a vars entry in play.yml.
Variables can also be split out into separate files.
Here is an example which shows both options:
- hosts: all
vars:
favcolor: blue
vars_files:
- /vars/external_vars.yml
tasks:
- name: this is just a placeholder
command: /bin/echo foo
https://docs.ansible.com/ansible/playbooks_variables.html#variable-file-separation
Ansible seems to delight in having different ways to do the same thing, without having either a nice comprehensive reference, or a rationale discussing the full implications of each different approach :). If you didn't remember the above was possible (I'd completely forgotten vars_files), the easiest option to find from the documentation might have been a third way, which is the most sophisticated one.
There's a prominent recommendation for ansible-examples. You can see a group_vars directory, with files which automatically provide values for hosts according to their groups, including the magic all group. The group_vars directory can be placed in the same directory as the playbook.
https://github.com/ansible/ansible-examples/tree/master/lamp_simple
Maybe this is what you want?
---
- hosts: testdroplets
roles:
- update
- bootstrap_server
- { role: create_new_user, username: 'foobar' }
- vimserver
https://docs.ansible.com/ansible/2.5/user_guide/playbooks_reuse_roles.html#using-roles
If you use include_role, variables can be passed like below.
- hosts: all_hosts
tasks:
- include_role:
name: "path/to/role"
vars:
var1: "var2_value"
var2: "var2_value"
Can't you just pass the variable from the command line with the -e parameter? So you can specifiy the variable even before execution. This also results in the strongest variable declaration which always takes precendence (see Variable precendence).
If you want to place it inside your playbook I suggest defining the username with the set_fact directive in the playbook. This variable is then available in all roles and included playbooks as well. Something like:
---
- hosts: testdroplets
pre_tasks:
- set_fact:
username: my_username
roles:
- update
- bootstrap_server
- create_new_user
- vimserver
It is all here: http://docs.ansible.com/ansible/playbooks_variables.html
while there are already some good answers, but I wanted to add mine because I've done this exact thing.
Here is the role I wrote: https://github.com/jmalacho/ansible-examples/tree/master/roles/users
And, I use hash_merge=true, and ansible's group_vars to make a dictionary of users: keys,groups so that adding a new user by host or by environment, and re-running is easy.
I also wrote up how my team uses group variables for environments once like this: "https://www.coveros.com/ansible-environment-design/"
I have two roles, one of which has a group_vars file that is vaulted, and another that is not. I would like to run the role that does not require any vaulted information, but ansible prompts me for a vault password anyway:
$ tree
├── deploy-home-secure.yml
├── deploy-home.yml
├── group_vars
│ ├── home
│ │ └── unvaulted
│ └── home-secure
│ ├── unvaulted
│ └── vaulted
├── hosts
└── roles
├── home
│ └── tasks
│ └── main.yaml
└── home-secure
└── tasks
└── main.yaml
$ ansible-playbook --version
ansible-playbook 1.8.2
configured module search path = None
$ ansible-playbook -i hosts deploy-home.yml
ERROR: A vault password must be specified to decrypt vaulttest/group_vars/home-secure/vaulted
$ ansible-playbook --vault-password-file=/dev/null -i hosts deploy-home.yml
ERROR: Decryption failed
I have something like this to solve this kind of problem (mine was not different roles, but different hosts, but I think the same principle applies):
This is the simplified file structure:
group_vars
development_vars
staging_vars
vaulted_vars
production_vars
This allows you to deploy development or staging without Ansible asking you to decrypt production_vars.
And then, the production playbook goes like this:
hosts: production
roles:
- role...
vars_files:
- vaulted_vars/production_vars
The vars_files line where you specify the path to the vaulted var is the key.
Ansible will try to load a group_vars file for any group it encounters in your inventory. If you split inventory file (hosts) into one for home group and another for home-secure then it will not try to decrypt vars it is not supposed to.
$ ansible-playbook -i hosts-home deploy-home.yml
$ ansible-playbook --ask-vault-password -i hosts-home-secure deploy-home-secure.yml
Here is another option if you don't always need your vaulted variables.
You can have a folder structure like this:
group_vars
├── all
├── prod
├── dev
│ └── vars.yml
└── dev-vault
└── vault.yml
You store vaulted variables in the '-vault' variant of that inventory.
Then your inventories might be something like the following:
dev:
[servers]
dev.bla.bla
[dev:children]
servers
dev-vault:
[servers]
dev.bla.bla
[dev:children]
servers
[dev-vault:children]
servers
So you're only saving sensitive data in the dev-vault vars, if in most cases you don't actually need passwords etc you can run playbooks without having extra options you're not really using, or storing the vault password in plaintext for convenience, etc.
So the "normal" command might be:
ansible-playbook -i dev some.yml
And the "vaulted" command could be:
ansible-playbook -i dev-vault some.yml --extra-vars="use_vault=true"
Or you could manage the "include vault variables" via tagging, including some.yml in some-vault.yml etc