Registering and using multiple variables in Ansible - ansible

I'm trying to pop VM instances, put them into different host groups (say webservers and devops/admin machines) and install what is needed on them in one single playbook.
I don't know what IP addresses, for instance, GCP will give these instances, and so i am trying to capture them in a variable for use later on in the playbook. I can capture them fine by using "register" but using them is proving tricky. For instance if I do.
- name: création des adresses statiques
gcp_compute_address:
name: "{{ item }}"
state: present
region: "{{ region }}"
project: "{{ gcp_project }}"
auth_kind: "{{ gcp_cred_kind }}"
service_account_file: "{{ gcp_cred_file }}"
scopes:
- https://www.googleapis.com/auth/compute
loop:
- adresse-1
- adresse-2
- adresse-3
- adresse-4
- adresse-5
register: address
The best way i have figured out to use these variables later on is:
network_interfaces:
- network: "{{ network.name }}"
access_configs:
- name: 'External NAT'
type: 'ONE_TO_ONE_NAT'
nat_ip:
- "{{ address.results[0].address }}"
- "{{ address.results[1].address }}"
- "{{ address.results[2].address }}"
Which fails miserably.
Please help ? How can I use the range of addresses I have created ?
I am going nuts over this

It is possible to add_host to the group webservers and proceed with the next play
- add_host:
name: "{{ item }}"
groups: webservers
loop: "{{ address.results|json_query('[*].address') }}"
- debug:
msg: "{{ groups['webservers'] }}"
- hosts: webservers
tasks:
- name: Configure cluster
...
The tasks below split the hosts into two groups
- set_fact:
my_hosts: "{{ address.results|json_query('[*].address') }}"
- add_host:
name: "{{ item }}"
groups: webservers1
loop: "{{ my_hosts[0:(my_hosts|length / 2)|int] }}"
- add_host:
name: "{{ item }}"
groups: webservers2
loop: "{{ my_hosts[(my_hosts|length / 2)|int:my_hosts|length] }}"
- debug:
msg: "{{ groups['webservers1'] }}"
- debug:
msg: "{{ groups['webservers2'] }}"
There is also GCE Dynamic Inventory and other 100+ gcp modules. You might want to start with Google Cloud Platform Guide.
FWIW, Ansible 2 Cloud Automation Cookbook covers leading providers incl. GCP.

Related

Conditional to add_host module

I have an inventory that i need to add a conditional to.
My code:
- name: Create memory inventory
add_host:
name: "{{ item.0.key }}"
group: target_hosts
with_nested:
- "{{ lookup ('dict', hosts, wantlist=True) }}"
But I want something like:
- name: Create memory inventory
add_host:
name: "{{ item.0.key }}"
{% if item.0.value.OS_Choice[:3] == 'win' %}
group:
- target_hosts
- win
{% else %}
group:
- target_hosts
- linux
{% endif %}
with_nested:
- "{{ lookup ('dict', hosts, wantlist=True) }}"
With this configuration, Ansible errors:
The offending line appears to be:
{% if item.0.value.OS_Choice[:3] == 'win' %}
^ here
Any ideas on how I can implement this conditional?
You're mixing up Jinja2 with YAML. Here you go:
- name: Create memory inventory when win
add_host:
name: "{{ item.0.key }}"
with_nested:
- "{{ lookup ('dict', hosts, wantlist=True) }}"
when: item.0.value.OS_Choice[:3] == 'win'
vars:
group:
- target_hosts
- win
- name: Create memory inventory when not win
add_host:
name: "{{ item.0.key }}"
with_nested:
- "{{ lookup ('dict', hosts, wantlist=True) }}"
when: item.0.value.OS_Choice[:3] != 'win'
vars:
group:
- target_hosts
- linux
However, Ansible gather facts about the OS already. Perhaps you want to use those, instead of configuring something like this yourself.
Building up on Kevin's answer (and fixing some wrongly placed parameters)
You should definitely do this differently, like creating dynamic groups based on detected OS in facts. See:
the group_by module
the ansible_distribution* facts that you can explore as an example with
ansible localhost -m setup -a filter="ansible_distribution*"
Meanwhile, with your current logic, you can still do this in a single task:
- name: Create memory inventory
vars:
additional_group: >-
{{ (item.0.value.OS_Choice[:3] == 'win') | ternary('win', 'linux') }}
add_host:
name: "{{ item.0.key }}"
groups:
- target_hosts
- "{{ additional_group }}"
with_nested:
- "{{ lookup ('dict', hosts, wantlist=True) }}"

Can we have 2 with_items in ansible in a single task

Below is the condition
- name: Find the image
slurp:
src: "{{ IMAGE }}"
register: slurp_results
- name: Upload image
shell: |
skopeo copy -docker-archive:{{ item }}.tar docker://{{ URL }}/TESTIMAGE
with_items: "{{ (slurp_results.content|b64decode).splitlines() }}"
The above code works.
But I would need "TESTIMAGE" also to be replaced as {{ item }} like below.
skopeo copy -docker-archive:{{ item }}.tar docker://{{ URL }}/{{ item }}
How to define 2 with_items in a single shell task with 2 different slurp results
I believe you can by using the subelements module. Here is a link. Try going by this example:
- name: Setup MySQL users, given the mysql hosts and privs subkey lists
mysql_user:
name: "{{ item.0.name }}"
password: "{{ item.0.mysql.password }}"
host: "{{ item.1 }}"
priv: "{{ item.0.mysql.privs | join('/') }}"
with_subelements:
- "{{ users }}"
- mysql.hosts
Users is referred to as item.0 and hosts as item.1 and so on.

Loop through variables in task that has another list embedded in the variables

I am looking to loop through a list of variables. I have it looping through the of variables using with_items, however the catch is there is a list within that variables list that needs to have a different subset / number of variables that i need to iterate through as well.
I have tried different filters to include with_nested, with_subelements, and with_items. I know that they are moving towards loops as the primary driver moving forward so any solution ideally would leverage the ansible path moving forward. I am looking at having an "inner" loop or an external task that will iterate through the vlans_list and input that data as its to that point.
group Variables
vnic_templates:
- name: vNIC-A
fabric: A
mac_pool: testmac1
mtu: 1500
org_dn: org-root
redundancy_type: none
state: present
template_type: initial-template
vlans_list: ### THE PROBLEM CHILD
- name: vlan2
native: 'no'
state: present
- name: vlan3
native: 'no'
state: present
The actual task - i have issues when i have to input multiple vlans. The vnic template will have a 1 to one relationship however the vlans_list could be 1 vnic_template to many vlans.
ucs_vnic_template:
hostname: "{{ ucs_manager_hostname }}"
username: "{{ ucs_manager_username }}"
password: "{{ ucs_manager_password }}"
name: "{{ item.name }}"
fabric: "{{ item.fabric }}"
mac_pool: "{{ item.mac_pool }}"
mtu: "{{ item.mtu }}"
org_dn: "{{ item.org_dn }}"
redundancy_type: "{{ item.redundancy_type }}"
state: "{{ item.state }}"
template_type: "{{ item.template_type }}"
vlans_list:
- name: "{{ item.1.name }}"
native: "{{ item.1.native }}"
state: "{{ item.1.present }}"
# loop: "{{ vnic_templates | subelements('vlans_list') }}"
with_items:
- "{{ vnic_templates }}"
I am starting down the road of adding an include vlan_list.yml outside of this task but no familiar with out to do that.
Actual results are
The task includes an option with an undefined variable. The error was: 'item' is undefined\n\n
I need the create a single vnic template with multiple vlans defined in that list.
Another engineer i work with was able to solve the question. By the way the variables are laid out we were able to easily just change the code
Change this:
vlans_list:
- name: "{{ item.1.name }}"
native: "{{ item.1.native }}"
state: "{{ item.1.present }}"
To this:
vlans_list: "{{ item.vlans_list }}"
Full Code listed below.
- name: Add vNIC Templates
ucs_vnic_template:
hostname: "{{ ucs_manager_hostname }}"
username: "{{ ucs_manager_username }}"
password: "{{ ucs_manager_password }}"
name: "{{ item.name }}"
fabric: "{{ item.fabric }}"
mac_pool: "{{ item.mac_pool }}"
mtu: "{{ item.mtu }}"
org_dn: "{{ item.org_dn }}"
redundancy_type: "{{ item.redundancy_type }}"
state: "{{ item.state }}"
template_type: "{{ item.template_type }}"
vlans_list: "{{ item.vlans_list }}"
with_items:
- "{{ vnic_templates }}"

Ansible: ec2_eni cannot attach interface

Using ansible I am trying to create ec2 instances and attach an extra network interface to each instance so that they will have two private IP addresses. However, for some reason, it seems that the ec2_eni module can create network interfaces, but will not attach them to the instances specified. What am I doing wrong? Below is my playbook:
---
- hosts: localhost
connection: local
gather_facts: false
tasks:
- name: Create new servers
ec2:
region: "{{ region }}"
vpc_subnet_id: "{{ subnet }}"
group_id: "{{ sec_group }}"
assign_public_ip: yes
wait: true
key_name: '{{ key }}'
instance_type: t2.micro
image: '{{ ami }}'
exact_count: '{{ count }}'
count_tag:
Name: "{{ server_name }}"
instance_tags:
Name: "{{ server_name }}"
register: ec2
- name: Show ec2 instance json data
debug:
msg: "{{ ec2['tagged_instances'] }}"
- name: allocate new elastic IPs and associate it with instances
ec2_eip:
region: "{{ region }}"
device_id: "{{ item['id'] }}"
with_items: "{{ ec2['tagged_instances'] }}"
register: eips
- name: Show eip instance json data
debug:
msg: "{{ eips['results'] }}"
- ec2_eni:
subnet_id: "{{ subnet }}"
state: present
secondary_private_ip_address_count: 1
security_groups: "{{ sec_group }}"
region: "{{ region }}"
device_index: 1
description: "test-eni"
instance_id: "{{ item['id'] }}"
with_items: "{{ ec2['tagged_instances'] }}"
The strange thing is that the ec2_eni task succeeds, saying that it has attached the network interface to each instance when in reality it just creates the network interface and then does nothing with it.
As best I can tell, since attached defaults to None, but the module docs say:
Specifies if network interface should be attached or detached from instance. If ommited, attachment status won't change
then the code does what they claim and skips the attachment step.
This appears to be a bug in the documentation, which claims the default is 'yes' but is not accurate.

Ansible Pass multiple vaules with single defined Variable

I need to add a server to service group every time I create a new server using the following task.
Task
- name: Create a service group
a10_service_group_v3:
validate_certs: no
host: "{{ item.0.a10_host }}"
state: "{{ item.1.service_state }}"
username: "{{ item.0.user }}"
password: "{{ item.0.pass }}"
service_group: "{{ item.1.group_name }}"
reset_on_server_selection_fail: yes
servers:
- name: "{{ item.1.server_name1 }}"
port: "{{ item.1.server_port1 }}"
overwrite: yes
write_config: yes
ignore_errors: yes
with_nested:
- "{{ a10 }}"
- "{{ service_group }}"
Variables:
service_group:
- group_name: bif_sg
service_state: present
server_name1: bif01
server_port1: 80
I need help with passing variables for server_name and server_port, let's say If I have 3 servers to add to service group in the task I need to add 3 times server_name1, server_port1
server_name2, server_port2 ......
Everytime I add server I need to update in the task as well :(
Is there a way to pass multiple times sever_name and serer_port with single defined value in the task.
I you expect server_group to have a list of servers, refactor your variable to have a list of servers and not a bunch of separate subkeys:
service_group:
- group_name: bif_sg
service_state: present
servers:
- name: bif01
port: 80
- name: bif02
port: 8080
And in your task:
...
servers: "{{ item.1.servers }}"
...

Resources