Let's say that I want to run something locally. but I want to use the vars from the hosts file, so basically - I want to do for each line something locally.
In this example, I want to use ec2_tag from ansible.
hosts file for ansible playbook run:
[any]
123.123.123.123 region=eu-region ec2_instance_id=x-xxxxxxxxxxxxxxxxx
123.123.123.124 region=eu-region ec2_instance_id=x-xxxxxxxxxxxxxxxxx
ansible-playbook:
- name: something
hosts: any
tasks:
- name: test
ec2_tag:
region: "{{ region }}"
resource: "{{ ec2_instance_id }}""
state: list
register: ec2_tags
- debug: msg={{ ec2_tags }}
How can i loop localy on [any] vars? let's say get region?
It's running now with local_action and taking the vars from the hosts file.
- name: something
hosts: any
tasks:
- name: test
local_action: ec2_tag region={{ region }} resource={{ ec2_instance_id }} state=list
register: ec2_tags
- debug: msg={{ ec2_tags }}
Related
I have the following task set:
- name: Initialise inventory_data variable
set_fact:
inventory_data: ""
- name: Get Instance Inventory
remote_user: me
ansible.builtin.script: scripts/inventory.sh
register: inventory
- name: Set inventory variable
set_fact:
inventory_data: "{{ inventory_data }} {{ inventory.stdout_lines | join('\n')}}"
- name: Send to API
remote_user: me
ansible.builtin.uri:
url: https://myapi.com/endpoint
method: POST
body: "{{ inventory_data }}"
status_code: 200
The desired result is that i need to gather the results from inventory.sh and send them only once at the end of the run.
I've tried different variations, with run_once, delegate_to etc.. but i cannot seem to get this!
Edit:
I am trying to gather some data from my script which is ran on every host, however i wish to aggregate the results from all hosts, and send it once to an API.
First, if your play looks something like this:
- hosts: all
tasks:
- name: Initialise inventory_data variable
set_fact:
inventory_data: ""
- name: Get Instance Inventory
remote_user: me
ansible.builtin.script: scripts/inventory.sh
register: inventory
- name: Set inventory variable
set_fact:
inventory_data: "{{ inventory_data }} {{ inventory.stdout_lines | join('\n')}}"
It's not going to do you any good. Your inventory.sh script will run on each host, which will set the inventory variable for that host, and the subsequent task will append inventory.stdout_lines to inventory_data for that host. This won't collect the output from multiple hosts. You need to restructure your playbook. First, run the inventory script on each host:
- hosts: all
gather_facts: false
tasks:
- name: Get Instance Inventory
ansible.builtin.script: scripts/inventory.sh
register: inventory
Then in a second play targeting localhost, build your merged inventory variable and send the data to the API:
- hosts: localhost
gather_facts: false
tasks:
- name: create merged inventory
set_fact:
inventory_data: "{{ inventory_data + hostvars[item].inventory.stdout }}"
vars:
inventory_data: ""
loop: "{{ groups.all }}"
- name: Send to API
remote_user: me
ansible.builtin.uri:
url: https://myapi.com/endpoint
method: POST
body: "{{ inventory_data }}"
status_code: 200
This way, (a) you build the inventory_data variable correctly and (b) you only make a single API call.
I've made a complete, runnable example of this solution available here.
I wrote an Ansible Playbook that creates multiple VMs. The Playbook is split in two files. Main.yaml and vars.yaml. Its creating the VMs and it seems to work nicely. Im not getting any errors so i assume it successfully added the created hosts to the Inventory. I want to check if the created hosts were added to the Inventory. How can i print/list the Hosts of the inventory? My goal is to run scripts at a later stage on the created VMs. Thanks.
**Main.yaml**
#########CREATING VM#########
---
- hosts: localhost
vars:
http_port: 80
max_clients: 200
vars_files:
- vars.yaml
tasks:
- name: create VM
os_server:
name: "{{ item.name }}"
state: present
image: "{{ item.image }}"
boot_from_volume: True
security_groups: ssh
flavor: "{{ item.flavor }}"
key_name: mykey
region_name: "{{ lookup('env', 'OS_REGION_NAME') }}"
nics:
- net-name: private
wait: yes
register: instances
with_items: "{{ instance_definitions }}"
############################################
- name: whait 15 seconds
pause: seconds=15
when: instances.changed
######DEBUG#################################
- name: display results
debug:
msg: "{{ item }}"
with_items: "{{ instances.results }}"
############################################
- name: Add new VM to ansible Inventory
add_host:
name: "{{ item.server.name}}"
ansible_host: "{{item.server.public_v4}}"
ansible_user: "{{ansible_user}}"
ansible_ssh_common_args: -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no
groups: just_created
with_items: "{{ instances.results }}"
**vars.yaml**
---
instance_definitions:
- { name: Debian Jessie, image: Debian Jessie 8, flavor: c1.small, loginame: debian }
- { name: Debian Stretch, image: Debian Stretch 9, flavor: c1.small, loginame: debian }
This is what magic variables are for.
All your hosts will be in a list:
groups['just_created']
The example below illustrates how to create in-memory hosts and list them. The magic sauce is that add_hosts needs to be in a separate play.
---
- name: adding host playbook
hosts: localhost
connection: local
tasks:
- name: add host to in-memory inventory
add_host:
name: awesome_host_name
groups: in_memory
- name: checking hosts
hosts: in_memory
connection: local
gather_facts: false
tasks:
- debug: var=group_names
- debug: msg="{{ inventory_hostname }}"
- debug: var=hostvars[inventory_hostname]
You can print the the content of the inventory file using from the playbook using:
- debug: msg="the hosts are is {{lookup('file', '/etc/ansible/hosts') }}"
Alternatively, you can list the hosts from command line using:
ansible --list-hosts all
or use this command from the playbook:
tasks:
- name: list hosts
command: ansible --list-hosts all
register: hosts
- debug:
msg: "{{hosts.stdout_lines}}"
I have a playbook that is supposed to create a config file for all specified hosts, on my monitoring_sever.
- hosts: all
gather_facts: True
hosts: monitoring_server
tasks:
- command: touch {{ hostvars[item]['ansible_fqdn'] }}
with_items: "{{ groups['all'] }}"
I execute the playbook with ansible-playbook main.yml -l "new_client, new_client2, monitoring_server"
The Resulting files on the monitoring server should look like the following:
client1.conf client2.conf
But I get an error about missing quotes, I've tried all sorts of syntax changes but I can't seem to find the problem.
Updated:
- hosts: all
gather_facts: True
tasks:
- file:
path: "{{ hostvars[item]['ansible_fqdn'] }}"
state: touch
delegate_to: host_name # Delegate task to specific host
with_items: "{{ groups['all'] }}"
You have typos in your original playbook.
with:items should be with_items
items should be item
Usage of delegate_to: http://docs.ansible.com/ansible/playbooks_delegation.html#delegation
As long as you are targeting all hosts, you don't have to make a loop as Ansible executes tasks on all targeted hosts unless excluded by condition.
On the other hand, I would suggest using file module instead of command to touch the file.
- hosts: all
tasks:
- name: Touch a file
file:
path: "{{ ansible_fqdn }}"
state: touch
PS. I assumed ansible_fqdn is a host var you defined for each host.
You need to fix:
with_items: instead of with:items:
item instead of items
a single hosts: declaration in each item on the list of plays
This should work for your case:
---
- hosts: all
gather_facts: true
- hosts: monitoring_server
tasks:
- command: touch {{ hostvars[item]['ansible_fqdn'] }}
with_items: "{{ groups['all'] }}"
Alternatively you can use delegate_to: localhost and completely drop the loop as well as the reference to hostvars:
---
- hosts: all
gather_facts: true
tasks:
- command: touch {{ ansible_fqdn }}
delegate_to: localhost
I have a need to know the index of host names in the inventory. I am using the below code to create a variable file that I can use in a subsequent play book
- name: Debug me
hosts: hosts
tasks:
- debug: msg="{{ inventory_hostname }}"
- debug: msg="{{ play_hosts.index(inventory_hostname) }}"
- local_action: 'lineinfile create=yes dest=/tmp/test.conf
line="host{{ play_hosts.index(inventory_hostname) }}=
{{ inventory_hostname }}"'
I have the following inventory file
[hosts]
my.host1.com
my.host2.com
Now when I run this, the test.conf that gets generated under /tmp sometimes has both hostnames like this
host1= my.host2.com
host0= my.host1.com
when I run the same playbook a few times each time emptying the test.conf before running. quite a few times the file only has one entry
host1= my.host2.com
or
host0= my.host1.com
how come the same ansible playbook behaving differently?
I believe the issue is your running two threads against different hosts, and using local_action is not thread safe.
Try using the serial keyword:
- name: Debug me
hosts: hosts
serial: 1
tasks:
- debug: msg="{{ inventory_hostname }}"
- debug: msg="{{ play_hosts.index(inventory_hostname) }}"
- local_action: 'lineinfile create=yes dest=/tmp/test.conf
line="host{{ play_hosts.index(inventory_hostname) }}=
{{ inventory_hostname }}"'
Edit: A better way to do this if just trying to operate on the list of host in inventory on the localhost, would be to avoid doing the action on the host and using local_action in the first place.
- name: Debug me
hosts: localhost
tasks:
- lineinfile:
create: yes
dest: /tmp/test.conf
line: "host{{ groups['hosts'].index(item)}}={{ item }}"
with_items: " {{ groups['hosts'] }}"
This will get you the results you desire. Then you can add another play to do operations against the hosts themselves.
The solution I am trying to avoid problems with race conditions with non-thread safe Local_action: lineinfile to write gathered data to local file. Split it across 2 different plays in the same file.
eg:
- name: gather_date
hosts: all
any_errors_fatal: false
gather_facts: no
tasks:
- name: get_Aptus_device_count_list
shell: gather_data.sh
become: true
register: Aptus_device_count_list
changed_when: false
- name: Log_gathered_date
hosts: all
any_errors_fatal: false
gather_facts: no
tasks:
- name: log_gathered_info
local_action:
module: lineinfile
dest: /home/rms-mit/MyAnsible/record_Device_count_collection.out
line: "\n--- {{ inventory_hostname }} --- \n
{{ Aptus_device_count_list.stdout }} \n.\n---\n"
changed_when: false
my below ansible (2.1.1.0) playbook is throwing "skipping: no hosts matched" error when playing for host [ec2-test]. The ansible hosts file has the fqdn of the newly created instance added. It runs fine if if i re run my playbook second time. but 1st time running throws no hosts matched error :(
my playbook:
---
- name: Provision an EC2 instance
hosts: localhost
connection: local
gather_facts: no
become: False
vars_files:
- awsdetails.yml
tasks:
- name: Launch the new EC2 Instance
ec2:
aws_access_key: "{{ aws_id }}"
aws_secret_key: "{{ aws_key }}"
group_id: "{{ security_group_id }}"
instance_type: "{{ instance_type }}"
image: "{{ image }}"
key_name: "{{ ssh_keyname }}"
wait: yes
region: "{{ region }}"
count: 1
register: ec2
- name: Update the ansible hosts file with new IP address
local_action: lineinfile dest="/etc/ansible/hosts" regexp={{ item.dns_name }} insertafter='\[ec2-test\]'line="{{item.dns_name}} ansible_ssh_private_key_file=/etc/ansible/e2-key-file.pem ansible_user=ec2-user"
with_items: ec2.instances
- name: Wait for SSH to come up
wait_for: host={{ item.public_dns_name }} port=22 delay=60 timeout=320 state=started
with_items: ec2.instances
- name: playing ec2-test instances
hosts: ec2-test
gather_facts: no
my hosts file has these inventories
[localhost]
localhost
....
[ec2-test]
ec2-54-244-180-186.us-west-2.compute.amazonaws.com
Any idea why i am getting the skipping: no hosts matched error if showing up here? any help would be greatly appreciated
Thanks!
It seems like Ansible reads the inventory file just once and does not refresh it after you add an entry into it during the playbook execution. Hence, it cannot add the newly added host.
To fix this you may force Ansible to refresh the entire inventory with the below task executed after you update the inventory file:
- name: Refresh inventory to ensure new instaces exist in inventory
meta: refresh_inventory
or you may not update the inventory file at all and use the add_host task instead:
- add_host:
name: "{{ item.dns_name }}"
groups: ec2-test
with_items: ec2.instances