Ansible in AWS, list processing question using ec2_instance_info for several nodes - amazon-web-services

I am running several Ansible playbooks in AWS and I am having difficulty with a test yaml file. The purpose of the yaml file is to query AWS for a list of servers using a filter and set_fact the instance name, the instance ID the instance size and the private IP.
The code I have is returning only data for the first node in the list and repeats the debug line every 12 lines. All the other lines show no data returned. I am using the ec2_instance_info to get the various data about the instances.
Here is the Ansible yaml file
---
# This script gathers the Instance ID's et. al.
- name: Get EC2 Info
ec2_instance_info:
region: '{{ aws_region }}'
aws_access_key: "{{ lookup('ini', 'aws_access_key_id section=saml file=~/.aws/credentials') }}"
aws_secret_key: "{{ lookup('ini', 'aws_secret_access_key section=saml file=~/.aws/credentials') }}"
security_token: "{{ lookup('ini', 'aws_session_token section=saml file=~/.aws/credentials') }}"
filters:
"tag:Name": "test-envMan*"
register: Instance_ID
- name: Get Instance ID
debug:
msg: "{{ item.0 }} | {{ item.1 }} | {{ item.2 }} | {{ item.3 }}"
with_together:
- "{{ Instance_ID.instances | map(attribute='tags.Name') | list }}"
- "{{ Instance_ID.instances[0].instance_id }}"
- "{{ Instance_ID.instances[1].instance_type }}"
- "{{ Instance_ID.instances[2].private_ip_address }}"
- name: Gather and Save info
set_fact:
Tag_Name: "{{ Instance_ID.instances | map(attribute='tags.Name') | list }}"
Instance_ID: "{{ Instance_ID.instances[0].instance_id }}"
Instance_Size: "{{ Instance_ID.instances[1].instance_type }}"
Instance_PrivIP: "{{ Instance_ID.instances[2].private_ip_address }}"
The output shows 12 lines of Ansible "ok" output for each server. The first line of which includes the debug output of the expected fields for the first node.
So 1 line of "ok" log output, then the debug line. Then 11 lines of "ok" log output of the same node. Then 1 line of "ok" output for the second node, the the debug line for the first node. etc.
I need to discover what I am doing incorrectly and how to make it behave.
Any comments, suggestions or pointers are appreciated.
Thanks.

Related

Ansible list has length when empty

I'm doing "aws rds describe-db-instances":
- name: list recovery points by resource
shell: aws rds describe-db-instances --db-instance-identifier {{ item }} --query "DBInstances[?(LatestRestorableTime>'{{ time }}')].[DBInstanceIdentifier,LatestRestorableTime]" --output text
loop: "{{ db_identifier }}"
register: recovery_points
environment:
AWS_ACCESS_KEY_ID: "{{ aws_access_key }}"
AWS_SECRET_ACCESS_KEY: "{{ aws_secret_key }}"
AWS_SESSION_TOKEN: "{{ security_token }}"
-->until: (recovery_points.results|map(attribute='stdout')|list|length) > 0
-->retries: 20
-->delay: 30
when: db_identifier is defined
That should check for recovery points older than "time", also db_identifier|lenght = 2.
- debug:
msg: "{{ recovery_points.results | map(attribute='stdout') | list }}"
result:
['', '']
The issue I'm facing is that when there are no recovery points and it returns this empty list, it says the length of this list is 2.
- debug:
msg: "{{ recovery_points.results | map(attribute='stdout') | list | length }}"
result:
2
Having this, I wanted to include the "until" condition in the shell command (that you can see preceded by -->) but I cannot do it because of this length.
How can I work around it?

Is it possible to loop into two different lists in the same playbook (Ansible)?

I'm writing a Playbook Ansible and I want to loop into two different lists.
I now that I can use with_items to loop in a list, but can I use with_items twice in the same playbook?
Here is what I want to do:
- name: Deploy the network in fabric 1 and fabric 2
tags: [merged]
role_network:
config:
- net_name: "{{ networkName }}"
vrf_name: "{{ vrf }}"
net_id: 30010
net_template: "{{ networkTemplate }}"
net_extension_template: "{{ networkExtensionTemplate }}"
vlan_id: "{{ vlan }}"
gw_ip_subnet: "{{ gw }}"
attach: "{{ item }}"
deploy: false
fabric: "{{ item }}"
state: merged
with_items:
- "{{ attachs }}"
"{{ fabric }}"
register: networks
So for the first call, I want to use the playbook with fabric[0] and attachs[0].
For the second call, I want to use the playbook with fabric[1] and attachs[1].
And so on...
Can someone help me please?
What you are looking to achieve is what was with_together and that is, now, recommanded to achieve with the zip filter.
So: loop: "{{ attachs | zip(fabric) | list }}".
Where the element of the first list (attachs) would be item.0 and the element of the second list (fabric) would be item.1.
- name: Deploy the network in fabric 1 and fabric 2
tags: [merged]
role_network:
config:
- net_name: "{{ networkName }}"
vrf_name: "{{ vrf }}"
net_id: 30010
net_template: "{{ networkTemplate }}"
net_extension_template: "{{ networkExtensionTemplate }}"
vlan_id: "{{ vlan }}"
gw_ip_subnet: "{{ gw }}"
attach: "{{ item.0 }}"
deploy: false
fabric: "{{ item.1 }}"
state: merged
loop: "{{ attachs | zip(fabric) | list }}"
register: networks

set_facts with dict as argument of a loop

I'd like to obtain the list of bridged interfaces grouped by master like this:
brv100:
- vnet0
- eth0
brv101:
- vnet1
- eth1
I want to use native json output from the shell commands.
The only thing I managed to do is to get a predefined number of interfaces like this:
- hosts: localhost
gather_facts: no
tasks:
- shell:
cmd: ip -details -pretty -json link show type bridge
register: list_bridges
- set_fact:
bridges: "{{ list_bridges.stdout }}"
- debug:
msg: "{{ bridges | map(attribute='ifname') | list}}"
- name: get json
shell:
cmd: ip -details -pretty -json link show master "{{ifname}}"
with_items: "{{bridges | map(attribute='ifname') | list}}"
loop_control:
loop_var: ifname
register: list_interfaces
- set_fact:
interfaces: "{{ list_interfaces.results | map(attribute='stdout') | list }}"
- set_fact:
toto: "{{interfaces.1}} + {{interfaces.2}}"
- debug:
msg: "{{toto | map(attribute='ifname')|list}}"
Now if I want to do the same with a loop :
- set_fact:
toto: " {{item|default([])}}+ {{ item |default([])}}.{{idx}} "
loop: "{{interfaces}}"
loop_control:
label: "{{item}}"
index_var: idx
- debug: var=toto
The result doesn't seem to be a list of list, but a list of strings and I can't extract the 'ifname' values with a simple debug
- debug:
msg: "{{toto | map(attribute='ifname')|list}}"
What am I supposed to do so as to get benefit of the json native output and get simple list of bridged interfaces (like brctl show was used to do)?
The lists of bridged interfaces grouped by the master are available in ansible_facts. The task below sets the dictionary of the bridges and bridged interfaces
- set_fact:
bridges: "{{ dict(ansible_facts|
dict2items|
json_query('[?value.type == `bridge`].[key, value.interfaces]')) }}"
Q: "Manage to get the same result manipulating JSON data."
A: The output of the ip -json ... command is JSON formated string which must be converted to JSON dictionary in Ansible by the from_yaml filter (JSON is a subset of YAML). For example, the tasks below give the same result
vars:
my_debug: false
tasks:
- name: Get bridges names
command: "ip -details -json link show type bridge"
register: list_bridges
- set_fact:
bridges: "{{ list_bridges.stdout|
from_yaml|
map(attribute='ifname')|
list }}"
- debug:
var: bridges
when: my_debug
- name: Get bridges interfaces
command: "ip -details -json link show master {{ item }}"
loop: "{{ bridges }}"
register: list_interfaces
- set_fact:
bridges_interfaces: "{{ list_interfaces.results|
json_query('[].stdout')|
map('from_yaml')|
list }}"
- debug:
msg: "{{ msg.split('\n') }}"
vars:
msg: "{{ item|to_nice_yaml }}"
loop: "{{ bridges_interfaces }}"
loop_control:
label: "{{ item|json_query('[].ifname') }}"
when: my_debug
- name: Set dictionary of bridges
set_fact:
bridges_dict: "{{ bridges_dict|
default({})|
combine({item.0: item.1|json_query('[].ifname')}) }}"
loop: "{{ bridges|zip(bridges_interfaces)|list }}"
loop_control:
label: "{{ item.1|json_query('[].ifname') }}"
- debug:
var: bridges_dict
Template to write the bridges to a file
{% for k,v in bridges_dict.items() %}
{{ k }}:
{% if v is iterable %}
{% for i in v %}
- {{ i }}
{% endfor %}
{% endif %}
{% endfor %}
- name: Write the bridges to file
template:
src: bridges.txt.j2
dest: bridges.txt
The file bridges.txt will be created in the remote host running the task.

Assign ansible vars based on AWS tags

I'm trying to figure out a way to assign variables in Ansible based on tags I have in AWS. I was experimenting with ec2_remote_tags but it's returning alot more information than I need. It seems like there should be an easier way to do this and I'm just not thinking of it.
For example, if I have a tag called function that creates the tag_function_api group using dynamic inventory and I want to assign a variable function to the value api. Any ideas on an efficient way to do this?
I've managed to make a dict of tags with lists of values:
- hosts: localhost
tasks:
- ec2_remote_facts:
region: eu-west-1
register: ec2_facts
# get all possible tag names
- set_fact: tags="{{ item.keys() }}"
with_items: "{{ ec2_facts.instances | map(attribute='tags') | list }}"
register: tmp_tags
# get flattened list of tags (for some reason lookup() returns string, so we use with_)
- assert: that=true
with_flattened: "{{ tmp_tags.results | map(attribute='ansible_facts.tags') | list }}"
register: tmp_tags
# get unique tag names
- set_fact: tags="{{ tmp_tags.results | map(attribute='item') | list | unique }}"
- set_fact: my_tags="{{ {} }}"
# get all possible values for a given tag
- set_fact:
my_tags: "{{ my_tags | combine( {''+item: ec2_facts.instances | map(attribute='tags.'+item) | select('defined') | list | unique}) }}"
with_items: "{{ tags }}"
- debug: var=my_tags
If you are using Ansible's ec2.py dynamic inventory script it makes all tags available as host variables in the form ec2_tag_<tag name> = <tag value>. It also adds all EC2 hosts to the group ec2.
So if your EC2 instance has a tag AwesomeVariable = "Greatness" and you want that value assigned to the Ansible host variable stupendous you can do this:
- name: Register variables based on tags
set_fact:
stupendous: "{{ ec2_tag_AwesomeVariable }}"
when: "'ec2' in group_names"
After this runs you can use the variable stupendous for your EC2 hosts and it has the value set for the AwesomeVariable tag.
Was able to get this to work based on some more information I found here: https://groups.google.com/forum/#!topic/ansible-project/ES2CjMPps3M
Here is the code that worked for us:
- name: Retrieve all tags on an instance
ec2_tag:
region: '{{ ec2_region }}'
resource: '{{ ec2_id }}'
state: list
aws_access_key: "{{ ANSIBLE_IAM_KEY }}"
aws_secret_key: "{{ ANSIBLE_IAM_SECRET }}"
register: ec2_facts
- name: register variables based on tag
set_fact:
tt_function: "{{ ec2_facts.tags.Function }}"
tt_release: "{{ ec2_facts.tags.Release }}"
tt_client: "{{ ec2_facts.tags.Client }}"

how to add a disk to vcenter guest using Ansible

I'm attempting to add a second disk to a vmware vcenter instance.
Here is what I have:
- name: "Modifying ..."
local_action:
module: vsphere_guest
vcenter_hostname: "{{ vcenter.hostname }}"
username: "{{ vcenter_user[datacenter]['username'] }}"
password: "{{ vcenter_user[datacenter]['password'] }}"
guest: "{{ inventory_hostname }}"
# Looky looky heeya ...#
state: reconfigured
########################
vm_extra_config:
vcpu.hotadd: yes
mem.hotadd: yes
notes: "{{ datacenter }} {{ purpose |replace('_',' ') }}"
vm_disk:
disk1:
size_gb: 50
type: thin
datastore: "{{ vcenter.datastore }}"
disk2:
size_gb: 200
type: thin
datastore: "{{ vcenter.datastore }}"
vm_hardware:
memory_mb: "{{ vm.memory|int }}"
num_cpus: "{{ vm.cpus|int }}"
osid: "{{ os.id }}"
esxi:
datacenter: "{{ esxi.datacenter }}"
hostname: "{{ esxi.hostname }}"
So the vcenter sees the reconfigure and there are no errors displayed.
Also there are no errors on the console when I runt the playbook.
It just simply does not add the second disk.
So is there a way to add the disk or will I have to write a python script to do it?
Thanks.
The function def reconfigure_vm in the vsphere_guest module does only include code for changing the RAM and the CPU. But i don't see any code for changing the other hardware. This is only possible while creating a new VM at the moment.