How to add host to group in Ansible Tower inventory? - ansible

How can I add a host to a group using tower_group or tower_host modules?
The following code creates a host and a group, but they are unrelated to each other:
---
- hosts: localhost
connection: local
gather_facts: false
tasks:
- tower_inventory:
name: My Inventory
organization: Default
state: present
tower_config_file: "~/tower_cli.cfg"
- tower_host:
name: myhost
inventory: My Inventory
state: present
tower_config_file: "~/tower_cli.cfg"
- tower_group:
name: mygroup
inventory: My Inventory
state: present
tower_config_file: "~/tower_cli.cfg"
Docs mention instance_filters parameter ("Comma-separated list of filter expressions for matching hosts."), however do not provide any usage example.
Adding instance_filters: myhost to the tower_group task has no effect.

I solved it using Ansible shell module and tower-cli. I Know that create a ansible module is better than it, but to a fast solution...
- hosts: awx
vars:
tasks:
- name: Create Inventory
tower_inventory:
name: "Foo Inventory"
description: "Our Foo Cloud Servers"
organization: "Default"
state: present
- name: Create Group
tower_group:
inventory: "Foo Inventory"
name: Testes
register: fs_group
- name: Create Host
tower_host:
inventory: "Foo Inventory"
name: "host"
register: fs_host
- name: Associate host group
shell: tower-cli host associate --host "{{fs_host.id}}" --group "> {{fs_group.id}}"

This isn't natively available in the modules included with Tower, which are older and use the deprecated tower-cli package.
But it is available in the newer AWX collection, which uses the awx CLI, as long as you have a recent enough Ansible (2.9 should be fine).
In essence, install the awx collection through a requirements file, or directly like
ansible-galaxy collection install awx.awx -p ./collections
Add the awx.awx collection to your playbook
collections:
- awx.awx
and then use the hosts: option to tower_group:.
- tower_group:
name: mygroup
inventory: My Inventory
hosts:
- myhost
state: present
You can see a demo playbook here.
Be aware though that you may need preserve_existing_hosts: True if your group already contains other hosts. Unfortunately there does not seem to be an easy way to remove a single host from a group.
In terms of your example this would probably work:
---
- hosts: localhost
connection: local
gather_facts: false
collections:
- awx.awx
tasks:
- tower_inventory:
name: My Inventory
organization: Default
state: present
tower_config_file: "~/tower_cli.cfg"
- tower_host:
name: myhost
inventory: My Inventory
state: present
tower_config_file: "~/tower_cli.cfg"
- tower_group:
name: mygroup
inventory: My Inventory
state: present
tower_config_file: "~/tower_cli.cfg"
hosts:
- myhost

Related

How do I tell Ansible to include localhost on the task?

I have this task:
- name: Install OpenJDK
become: true
apt:
name: openjdk-8-jre-headless
cache_valid_time: 60
state: latest
I want to run it in all hosts, including localhost. How can I tell Ansible to include localhost in the hosts for just one play?
You just add localhost to the pattern of targeted hosts in your play. Note that, unless your re-define it in your inventory, localhost is implicit and does not match the all special group.
The global idea
---
- name: This play will target all hosts in inventory
hosts: all
tasks:
- debug:
msg: I'm a dummy task
- name: This play will target all inventory hosts AND implicit localhost
hosts: all:localhost
tasks:
- debug:
msg: Yet an other dummy task

Ansible Dynamic Inventory

I'm running a playbook which houses multiple roles targets multiple hosts
The goal is to deploy a VM and use it's IP to deploy an app.
My playbook, has two roles, using "build_vm" role I'm able to display IP address via debug, yet when passing ipaddr variable to second role, Ansible complains that the variable is not defined
- hosts: linux
become: true
roles:
- build_vm
- tasks:
- debug: msg="{{ ipaddr }}"
- hosts: "{{ ipaddr }}"
roles:
- deploy_app
I have used set_fact with and ran into same issue, I wonder what I should be using here? dynamic inventory? I have searched sparse docs online and I'm unable to find an intuitive example to follow.
There are many ways to using add_host. In this example, I am adding the new host to a group and using it in a later play.
- hosts: linux
become: true
roles:
- build_vm
- tasks:
- debug: msg="{{ ipaddr }}"
- name: Add ipaddr to host inventory
add_host: name="{{ ipaddr }}" group=NewHostGroup
- hosts: NewHostGroup
roles:
- deploy_app

how to include extra package in ansible?

for example, want to use gluster_volume module.
with ansible 2.1 I get
'gluster_volume' is not a valid attribute for a Play.
How do I "include" it in my ansible?
---
- name: create gluster volume
hosts: all
gluster_volume: state=present name=test1 bricks=/bricks/brick1/g1 rebalance=yes
run_once: true
Can you try this:
---
- name: create gluster volume
hosts: all
tasks:
- gluster_volume: state=present name=test1 bricks=/bricks/brick1/g1 rebalance=yes
run_once: true

how to run a particular task on specific host in ansible

my inventory file's contents -
[webservers]
x.x.x.x ansible_ssh_user=ubuntu
[dbservers]
x.x.x.x ansible_ssh_user=ubuntu
in my tasks file which is in common role i.e. it will run on both hosts but I want to run a following task on host webservers not in dbservers which is defined in inventory file
- name: Install required packages
apt: name={{ item }} state=present
with_items:
- '{{ programs }}'
become: yes
tags: programs
is when module helpful or there is any other way? How could I do this ?
If you want to run your role on all hosts but only a single task limited to the webservers group, then - like you already suggested - when is your friend.
You could define a condition like:
when: inventory_hostname in groups['webservers']
Thank you, this helps me too.
hosts file:
[production]
host1.dns.name
[internal]
host2.dns.name
requirements.yml file:
- name: install the sphinx-search rpm from a remote repo on x86_64 - internal host
when: inventory_hostname in groups['internal']
yum:
name: http://sphinxsearch.com/files/sphinx-2.2.11-1.rhel7.x86_64.rpm
state: present
- name: install the sphinx-search rpm from a remote repo on i386 - Production
when: inventory_hostname in groups['production']
yum:
name: http://sphinxsearch.com/files/sphinx-2.2.11-2.rhel6.i386.rpm
state: present
An alternative to consider in some scenarios is -
delegate_to: hostname
There is also this example form the ansible docs, to loop over a group. https://docs.ansible.com/ansible/latest/user_guide/playbooks_delegation.html -
- hosts: app_servers
tasks:
- name: gather facts from db servers
setup:
delegate_to: "{{item}}"
delegate_facts: True
loop: "{{groups['dbservers']}}"

Ansible run task once per database-name

I'm using ansible to deploy several sites to the same server. Each site is a separate 'host' in the ansible hosts inventory, which works really well.
However, there are only two databases: production and testing.
How can I make sure my database-migration task only runs once per database?
I've read into the group_by, run_once and delegate_to features, but I'm not sure how to combine those.
The hosts look something like:
[production]
site1.example.com ansible_ssh_host=webserver.example.com
site2.example.com ansible_ssh_host=webserver.example.com
[beta]
beta-site1.example.com ansible_ssh_host=webserver.example.com
beta-site2.example.com ansible_ssh_host=webserver.example.com
[all:children]
production
beta
The current playbook looks like this:
---
- hosts: all
- tasks:
# ...
- name: "postgres: Create PostgreSQL database"
sudo: yes
sudo_user: postgres
postgresql_db: db="{{ DATABASES.default.NAME }}" state=present template=template0 encoding='UTF-8' lc_collate='en_US.UTF-8' lc_ctype='en_US.UTF-8'
tags: postgres
register: createdb
delegate_to: "{{ DATABASES.default.HOST|default(inventory_hostname) }}"
# ...
- name: "django-post: Create Django database tables (migrate)"
django_manage: command=migrate app_path={{ src_dir }} settings={{ item.settings }} virtualenv={{ venv_dir }}
with_items: django_projects
#run_once: true
tags:
- django-post
- django-db
- migrate
The best way I found was to restrict the execution of a task to the first host of a group. Therefore you need to add the groupname and the databases to a group_vars file like:
group_vars/production
---
dbtype=production
django_projects:
- name: project_1
settings: ...
- name: project_n
settings: ...
group_vars/beta
---
dbtype=beta
django_projects:
- name: project_1
settings: ...
- name: project_n
settings: ...
hosts
[production]
site1.example.com ansible_ssh_host=localhost ansible_connection=local
site2.example.com ansible_ssh_host=localhost ansible_connection=local
[beta]
beta-site1.example.com ansible_ssh_host=localhost ansible_connection=local
beta-site2.example.com ansible_ssh_host=localhost ansible_connection=local
[all:children]
production
beta
and limit the task execution to the first host that matches that group:
- name: "django-post: Create Django database tables (migrate)"
django_manage: command=migrate app_path={{ src_dir }} settings={{ item.settings }} virtualenv={{ venv_dir }}
with_items: django_projects
when: groups[dbtype][0] == inventory_hostname
tags:
- django-post
- django-db
- migrate
So, the below will illustrate why I say "once per group" is generally not supported. Naturally, I would love to see a cleaner out-of-the-box way, and do let me know if "once per group" isn't what you're after.
Hosts:
[production]
site1.example.com ansible_ssh_host=localhost ansible_connection=local
site2.example.com ansible_ssh_host=localhost ansible_connection=local
[beta]
beta-site1.example.com ansible_ssh_host=localhost ansible_connection=local
beta-site2.example.com ansible_ssh_host=localhost ansible_connection=local
[beta:vars]
dbhost=beta-site1.example.com
[production:vars]
dbhost=site1.example.com
[all:children]
production
beta
Example playbook which will do something on the dbhost once per group (the two real groups):
---
- hosts: all
tasks:
- name: "do this once per group"
sudo: yes
delegate_to: localhost
debug:
msg: "do something on {{hostvars[groups[item.key].0]['dbhost']}} for {{item}}"
register: create_db
run_once: yes
with_dict: groups
when: item.key not in ['all', 'ungrouped']

Resources