Below is my directory structure for ansibel role called webserver
localhost roles # tree
.
├── readme.md
├── site.yml
└── webserver
├── files
│ ├── createswap.sh
│ └── nginxkeyadd.sh
├── handlers
│ └── main.yml
├── tasks
│ └── main.yml
├── templates
│ ├── helloworld.conf.j2
│ └── index.html.j2
└── vars
└── main.yml
my tasks/main.yml looks like
- name: Create swap file 50MB
script: /etc/ansible/roles/webserver/files/createswap.sh
- name: add GPG key for nginx
script: /etc/ansible/roles/webserver/files/nginxkeyadd.sh
- name: Install nginx on target
apt: name={{ item }} state=latest
with_items:
- rsync
- git
- nginx
in the task/main.yml im specifying absolute path to local scriptfile like
script: /etc/ansible/roles/webserver/files/nginxkeyadd.sh and script: /etc/ansible/roles/webserver/files/createswap.sh . The scripts don't have any ansible variables.
is it good practice in ansible ?
is it good practice in ansible ?
No. Excerpt from the docs:
Any copy, script, template or include tasks (in the role) can reference files in roles/x/{files,templates,tasks}/ (dir depends on task) without having to path them relatively or absolutely
Also using shell-scripts instead of native Ansible modules is an antipattern.
Related
I want to extend my current Ansible project to also support Linux servers. For that I want to re-use the vault file I have created but I cannot seem to find a solution without duplicating the vault file.
Here's what my current Ansible structure looks like
├── ansible.cfg
├── ansible_pw.sh
├── group_vars
│ └── windows
│ ├── vault.yml
│ └── main.yml
├── inventory.yml
├── main.yml
└── roles
├── wait_for_host
│ └── tasks
│ └── main.yml
└── install_software
└── tasks
└── main.yml
inventory.yml
---
all:
children:
windows:
hosts:
win-server.mycompany.com
main.yml
---
- hosts: windows
tasks:
- block:
- include_role: { name: wait_for_host }
- include_role: { name: install_software }
Playbook is run like this:
ansible-playbook main.yml -i inventory.yml --vault-password-file ./ansible_pw.sh
My idea is to create a new group_vars/linux directory which contains all specific settings which only apply for linux servers.
While writing this question I actually found neat solution. All general settings (including the vault file) can be stored in the default all group (see https://docs.ansible.com/ansible/latest/user_guide/intro_inventory.html#default-groups) and all Windows/Linux specific settings (like ansible_connection) can be stored in separate directories:
group_vars
├── all
│ ├── main.yml
│ └── vault.yml
├── linux
│ └── main.yml
└── windows
└── main.yml
I'm trying to clean up my ansible infrastructure and wanted to stick to the best practices as much as possible.
My problem is that I have host-specific tasks that I naturally want to map into the logic as sensibly as possible.
If I want to have both, "best practice" and host-specific tasks, I end up with the following simplified file structure:
├── dbservers.yml
├── generic.yml
├── group_templates/
│ └── webservers/
├── group_vars/
│ └── webservers.yml
├── host_files/
│ └── d-ws323/
├── host_tasks/
│ └── d-ws323.yml
├── host_vars/
│ └── d-ws323/
├── production.ini
├── roles/
├── secrets.txt
├── site.yml
└── webservers.yml
My approch looks like this:
site.yml is the master playbook:
---
# file: site.yml
- import_playbook: webservers.yml
- import_playbook: dbservers.yml
- import_playbook: generic.yml
And generic.yml:
---
- hosts: all
become: yes
roles:
- generic
tasks:
- local_action: stat path=host_tasks/{{ inventory_hostname }}.yml
register: host_file
become: no
- include: host_playbooks/{{ inventory_hostname }}.yml
when: host_file.stat.exists
Does it make sense?
What we do is create roles that have playbooks and then assign these roles to hosts or groups of hosts. Based on that, we would have a directory layout similar to:
.
├── group_vars
├── hosts
├── host_vars
│ └── host-01.yml
├── roles
│ └── webservers
│ ├── files
│ ├── tasks
│ │ └── main.yml
│ └── templates
└── webservers.yml
The hosts file is the inventory file, which you're gonna put all your hosts and groups, and the webservers.yml is the playbook with the following content:
---
- hosts: host-01
become: true
user: ubuntu
roles:
- webservers
If you have to make any distro-specific arrangement, you should do that inside the role playbook. Playbooks outside roles should exist only for conecting groups or hosts to roles, or very trivial tasks, if possible.
Best regards!
I am trying to use Ansible Collection for example the nginx one.
The directory tree structure looks like this:
├── ansible_collections
│ └── nginxinc
│ └── nginx_core
│ ├── CHANGELOG.md
.......
│ ├── README.md
│ └── roles
│ ├── nginx
│ │ ├── tasks
│ │ │ ├── amplify
│ │ │ ├── config
│ │ │ ├── keys
│ │ │ ├── main.yml
│ │ │ ├── modules
│ │ │ ├── opensource
│ │ │ │ └── install-debian.yml
│ │ │ └── unit
....
├── hosts
└── site.yaml
the site.yaml file I wrote is:
- name: Demo
hosts: all
connection: local
gather_facts: no
tasks:
- name: test
include_role:
name: nginxinc.nginx_core.nginx
tasks_from: install-debian
I am trying to run the task install-debian from the role nginx.
I run the playbook:
ansible-playbook -i hosts site.yaml
I get this error:
ERROR! the role 'nginxinc.nginx_core.nginx' was not found.....
I need help on how I should fix the site.yaml file
If I understand correctly, you should just install the Nginx collection with the following command, as explained here:
ansible-galaxy collection install nginxinc.nginx_core
It should install it in ~/.ansible/collections/ansible_collections/nginxinc/nginx_core/. Then create a playbook following these examples and the Ansible docs:
---
- hosts: all
collections:
- nginxinc.nginx_core
roles:
- role: nginx
Finally run your playbook:
ansible-playbook -i hosts my_nginx_playbook.yaml
It'll pick the Debian version for you if your host is Debian.
I regret to say that this is not working for me. this gives the impression that collections_paths is not used.
ansible --version
ansible 2.9.17
ansible-config view
[defaults]
inventory=/usr/local/ansible-admin/my_hosts
roles_path=/usr/local/galaxy-roles:./roles
collections_paths=/usr/local/ansible_collections
log_path=./ansible.log
the collections are installed in the /usr/local/ansible_collections folder:
tree -L 2 /usr/local/ansible_collections/nginxinc/
/usr/local/ansible_collections/nginxinc/
└── nginx_core
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── docs
├── FILES.json
├── LICENSE
├── MANIFEST.json
├── playbooks
├── plugins
├── README.md
└── roles
here is the very basic content of the playbook:
cat playbooks/nginx_core.yml
- name: Test collection ansible with nginxinc
hosts: "{{ target }}"
collections:
- nginxinc.nginx_core
tasks:
- import_role:
name: nginx
we get the following error message when it is launched:
ansible-playbook playbooks/nginx_core.yml --extra-vars target=myvm.mydomain.org
ERROR! the role 'nginx' was not found in nginxinc.nginx_core:ansible.legacy:/usr/local/ansible-admin/playbooks/roles:/usr/local/galaxy-roles:/usr/local/ansible-admin/roles:/usr/local/ansible-admin/playbooks
it doesn't find the role in the collections, and worse, it doesn't say that it looked in the collections_path...
But here is a solution that works, but it is very very ugly: add the nginx role of the collection in roles_path!
roles_path=/usr/local/galaxy-roles:./roles:/usr/local/ansible_collections/nginxinc/nginx_core/roles
warning: this is obviously a misuse of the ansible collections!
any help would be appreciated.
Ernest.
Im trying to build a very simple plugin filter within a role. However, when I go to test the plugin it doesnt detect it.
Ansible version is 2.9.
dir
(base) root#8c08139d265e:/workspace# tree
.
├── Dockerfile
├── Makefile
├── README.md
├── defaults
│ └── main.yml
├── filter_plugins
│ ├── __init__.py
│ └── sample_filter.py
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── requirements.txt
├── tasks
│ └── main.yml
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
Plugin
(base) root#8c08139d265e:/workspace# cat filter_plugins/sample_filter.py
class FilterModule(object):
def filters(self):
return {'cloud_truth': cloud_truth}
def cloud_truth(a):
print(type(a))
return a.replace("the cloud", "somebody else's computer")
test
(base) root#8c08139d265e:/workspace# cat tests/test.yml
---
- name: test cloud_truth filter
hosts: localhost
roles:
- .
vars:
statement: "I store my files in the cloud"
tasks:
- name: make a statement
debug:
msg: "{{ statement | cloud_truth }}"
error
TASK [make a statement] *****************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "template error while templating string: no filter named 'cloud_truth'. String: {{ statement | cloud_truth }}"}
Thanks,
The problem seems to be related to where the filter plugin is in relation to your playbook.
In order for your playbook to work, the filter_plugins folder needs to be inside the tests folder:
.
└── tests
├── filter_plugins
│ ├── __init__.py
│ └── sample_filter.py
└── test.yml
However, this then causes the problem that the filter is no longer available to other tasks. The alternative would be to perhaps use roles and a structure like this:
.
├── filter_plugins
│ ├── __init__.py
│ └── sample_filter.py
├── playbook.yml
└── roles
└── tests
└── tasks
└── main.yml
But of course, this depends on the framework you're trying to build.
Managed to resolve this by installing itself as a role. Like so:
ansible-galaxy install git+git#github.com:<user>/ansible-role-networking.git
Then add the role within test.yml.
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>