I have a task which copies a default script file to the destination:
- name: copy keepalive state script
copy:
src: 'keepalived.state.sh'
dest: /usr/local/bin/keepalived.state.sh
mode: '0755'
owner: root
group: root
And now I want to have scripts based on the group that the host is in, so I changed the task to this:
- name: copy keepalive state script
copy:
src: '{{keepalived_state_script_file}}'
dest: /usr/local/bin/keepalived.state.sh
mode: '0755'
owner: root
group: root
vars:
keepalived_state_script_file: "{{ lookup('first_found', dict(files=['keepalived/' + item + '.state.sh', 'keepalive.state.sh'])) }}"
with_items: "{{group_names}}"
so now for a host inside the application group, if I put the application.state.sh file inside the keepalived directory, it will copy this file instead of default keepalived.state.sh, but If my host is a member of more than one group like this:
[application]
host1
[dc1]
host1
this task will first checks the application group and copies the application.state.sh then checks the dc1 group and since there is no dc1.state.sh it will copy the default script file which is keepalived.state.sh.
Considering I will use only one script file per host no matter how many groups that host is member of, how can I fix the problem so that I can get the customized script instead of the default script?
Your solution is way too complicated. You try to invent a feature, that Ansibles has already implemented: group vars.
Put the files application.yml and dc1.yml in the group_vars directory.
For the application group you set one value:
files_to_copy:
- file1
- file2
And for the dc1 group you set other values:
files_to_copy:
- file3
- file4
And in your playbook you iterate just over the files_to_copy variable. Ansible will take care, that every host gets the right variable based on the group membership.
Related
When I run my Ansible playbook, I define my hosts which looks to a group in my inventory.
$ ansible-playbook -i inv/hosts conf.yml
conf.yml:
- name: Configure QA Nodes
hosts: conf_qa
inv/hosts:
[conf_qa]
confqa1
# confqa2
[conf_prod]
prod1
# prod2
prod3
Is there a way in my Roles (or other elements of the Playbook) where I can back out which group_name (or equivalent) is being used?
I know I could set a variable in group_vars/conf_qa.yml such as qa: true and then reference it later in my Roles
roles/init/tasks/main.yml:
- name: Do this when dealing with the conf_qa group
when: qa == true
But using group_vars/conf_qa.yml seems like an extra intermediary step when I was hoping to reference the host groups more directly. Is there a better way?
You can add the following condition, this is from a playbook I created and I can confirm that it works.
It only runs the task in the servers that belong to that group, the rest of them will appear as "skipped"
- name: create api folder
file:
path: /var/log/api
state: directory
when: inventory_hostname in groups['switch']
I'm trying to separate my vars from the playbook file.
My vars use an alias to make things easier.
Example of my playbook.yaml:
---
- hosts: localhost
name: playbook name
vars:
login: &login
username: "{{ login.user }}"
password: "{{ login.pass }}"
vars_files:
- creds.yaml
tasks:
- name: using creds
some_module:
some_command: true
<<: *login
How can I move the "vars" section to a file outside the playbook and keep the same template inside "tasks"?
Your way to achieve that is to create a proper inventory and use the inventory variables. ansible inventory docs
Create this schema tree:
hosts (directory)
group_vars (directory)
host_vars (directory)
hosts-groups (directory)
hosts-all (file)
Then either you choose to define your hosts individually in the hosts-all
localhost ansible_ssh_host=x.x.x.x {{ potentially any other needed argument/variable }}
Or You group hosts in groups and define new group in hosts/hosts-groups: Create a file with the desired group name, ex: hosts/hosts-groups/local and inside it define your host-group
[local]
localhost
And then, either in the hosts_var or group_vars (depending on your first step), you create a file either with host name or group name, and inside it, you write your variables. Ex (by group): Create hosts/group_vars/local and inside it write your variables
login: user1
pass: pass1
And then automatically, your play will search the right vars for each hosts according to your inventory structure.
I'm currently trying to get used to Ansible but I'm failing to achieve what seems to be a common use-case:
Lets say I have have a role nginx in roles/nginx and and one task is to setup a custom default page:
- name: install nginx default page
copy:
src: "index.html"
dest: /var/www/html/
owner: root
mode: 0644
Ansible will look for the file in:
roles/nginx/files
roles/nginx
roles/nginx/tasks/files
roles/nginx/tasks
files
./
Now for some reason a single host should receive a completely different file.
I know I could alter the file src path to src: "{{ inventory_hostname }}/index.html" but then it would search in host-specific directories only.
Is there a way to alter the search paths so that Ansible will look for files in host-specific directories first but fall-back to common directories?
I don't want to decide if files might need to be host-specific when writing roles. I'd rather like to overwrite the role default files without altering the base role at all.
Q: "Is there a way to alter the search paths so that Ansible will look for files in host-specific directories first but fall back to common directories?"
A: In general, it is not possible to change the search paths. But, with first_found it is possible to define how a specific file shall be searched. For example,
- copy:
src: "{{ lookup('first_found', findme) }}"
dest: /scratch/tmp/
owner: root
mode: 0644
vars:
findme:
- "{{ inventory_hostname }}/index.html"
- "{{ role_path }}/files/index.html"
- "{{ role_path }}/files/defaults/index.html"
Given a bunch of servers, A..Z, what's the best way of ensuring that all /dir/path directories on these servers (where these directories exist) are set to a certain owner, group and mode?
I can see how to do it for a specific role, e.g.
- name: Add apache vhosts configuration.
template:
src: "vhosts.conf.j2"
dest: "{{ apache_conf_path }}/{{ apache_vhosts_filename }}"
owner: root
group: root
mode: 0644
notify: restart apache
when: apache_create_vhosts
but how do you do it across a whole range of servers?
http://docs.ansible.com/ansible/latest/file_module.html
- name: Change ownership of the folder
file:
state : directory
recurse : yes
path : "{{ folder }}"
mode : "{{ desired_mode }}"
Execute the task on all the systems you want changed.
Obviously, run it as the necessary user; if that's root, make sure you specify owner and group if needed.
Forgive me if this seems a bit basic, but sometimes it's nice to have the obvious reiterated - http://docs.ansible.com/ansible/latest/intro_inventory.html
To run against a list of servers, put them in a group in your inventory file and call the task on that group as hosts.
Hosts file example:
[targetServers]
host1
host2
# etc
then in your main.yml
- name: do the thing
hosts: targetServers
# etc
then ansible-playbook -i hosts -v main.yml oe some such.
In order to perform some operations locally (not on the remote machine), I need to put the content of an ansible variable inside a temporary file.
Please note that I am looking for a solution that takes care of generating the temporary file to a location where it can be written (no hardcoded names) and also that takes care of the removal of the file as we do not want to leave things behind.
You should be able to use the tempfile module, followed by either the copy or template modules. Like so:
- hosts: localhost
tasks:
# Create a file named ansible.{random}.config
- tempfile:
state: file
suffix: config
register: temp_config
# Render template content to it
- template:
src: templates/configfile.j2
dest: "{{ temp_config.path }}"
vars:
username: admin
Or if you're running it in a role:
- tempfile:
state: file
suffix: config
register: temp_config
- copy:
content: "{{ lookup('template', 'configfile.j2') }}"
dest: "{{ temp_config.path }}"
vars:
username: admin
Then just pass temp_config.path to whatever module you need to pass the file to.
It's not a great solution, but the alternative is writing a custom module to do it in one step.
Rather than do it with a file, why not just use the environment? This wan you can easily work with the variable and it will be alive through the ansible session and you can easily retrieve it in any steps or outside of them.
Although using the shell/application environment is probably, if you specifically want to use a file to store variable data you could do something like this
- hosts: server1
tasks:
- shell: cat /etc/file.txt
register: some_data
- local_action: copy dest=/tmp/foo.txt content="{{some_data.stdout}}"
- hosts: localhost
tasks:
- set_fact: some_data="{{ lookup('file', '/tmp/foo.txt') }}"
- debug: var=some_data
As for your requirement to give the file a unique name and clean it up at the end of the play. I'll leave that implementation to you