Error getting an item from json file in Ansible - azure

I am trying to get the subnets from a json query in Ansible. My debug is result is as below:
"msg": [
{
"address_prefix": "10.10.2.0/23",
"id": "/subscriptions/xxxxxxxxx-xxxxxxxxxxx-xxxxxxxx-xxx/resourceGroups/myRG/providers/Microsoft.Network/virtualNetworks/MYVNET/subnets/MySubnet",
"name": "MySubnet",
"network_security_group": "/subscriptions/xxxxxxxxx-xxxxxxxxxxx-xxxxxxxx-xxx/resourceGroups/MYVNET/providers/Microsoft.Network/networkSecurityGroups/mynsg",
"provisioning_state": "Succeeded",
"route_table": null
}
]
I am trying to get the name of the subnet. My Ansible play is as below:
tasks:
- name: "Retrieve resourcegroup infos"
azure_rm_virtualnetwork_info:
register: object_vnet
- name: get subnet name
debug: msg='{{ item.value }}'
with_dict: "{{ object_vnet.virtualnetworks }}"
when:
- item.key == 'subnets'
- item.value.name is match *MySub*
The error that I get , is as below:
fatal: [localhost]: FAILED! => {"msg": "The conditional check 'item.value.name is match *-MySub*' failed.
The error was: template error while templating string: unexpected 'end of statement block'.
String: {% if item.value.name is match *MySub* %} True {% else %} False {% endif %}\n\n
The error appears to be in '/myplay.yml': line 41, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\n
The offending line appears to be:\n\n\n - name: get subnet name\n ^ here\n"}
Any ideas to help?

subnets are a list, so you need to loop on subnets objects of your vnets and loop on all subnets in a vnet.
to do this, you can create a separate file to loop and you subnets and loop on this file:
loopfile:
- name: create facts azure_subnets_result
set_fact:
azure_subnets_result: >-
{{
( azure_subnets_result | default([]) )
+ [ az_sub_item ]
}}
loop_control:
loop_var: az_sub_item
loop: "{{ az_sub }}"
when: (az_sub_item.name | lower ) is match(".*mysub.*")
main file:
tasks:
- name: "Retrieve resourcegroup infos"
azure_rm_virtualnetwork_info:
register: object_vnet
- name: create azure_subnets object
set_fact:
azure_subnets: "{{ object_vnet.virtualnetworks | json_query(vnet_query) }}"
vars:
vnet_query: "[*].subnets"
- name: loop on all object subnets and all sub objects
include_tasks: loop.yml
loop_control:
loop_var: az_sub
loop: "{{ azure_subnets }}"
- name: debug message
debug: msg='{{ azure_subnets_result }}'

Related

Multiple with_items in an Ansible module block

I want to create multiple logical volumes with a variable file but it return a sintax error found character that cannot start any token, I have tried in different ways but still doesn't work
main.yml
---
- name: playbook for create volume groups
hosts: localhost
become: true
tasks:
- include_vars: vars.yml
- name: Create a logical volume
lvol:
vg: vg03
lv: "{{ item.var1 }}"
size: "{{ item.var2 }}"
with_items:
- { var1: "{{ var_lv_name }}", var2: "{{ var_lv_size }}" }
vars.yml
var_lv_name:
- lv05
- lv06
var_lv_size:
- 1g
- 1g
Use with_together. Test it first. For example,
- debug:
msg: "Create lv: {{ item.0 }} size: {{ item.1 }}"
with_together:
- "{{ var_lv_name }}"
- "{{ var_lv_size }}"
gives (abridged)
msg: 'Create lv: lv05 size: 1g'
msg: 'Create lv: lv06 size: 1g'
Optionally, put the declaration below into the file vars.yml
var_lv: "{{ var_lv_name|zip(var_lv_size) }}"
This creates the list
var_lv:
- [lv05, 1g]
- [lv06, 1g]
Use it in the code. The simplified task below gives the same results
- debug:
msg: "Create lv: {{ item.0 }} size: {{ item.1 }}"
loop: "{{ var_lv }}"
The previous answer it's totally correct but In my humble opinion we should be getting into the new way to do the things with loop and filters.
Here's my answer:
---
- name: playbook for create volume groups
hosts: localhost
gather_facts: no
become: true
vars_files: vars.yml
tasks:
- name: Create a logical volume
lvol:
vg: vg03
lv: "{{ item[0] }}"
size: "{{ item[1] }}"
loop: "{{ var_lv_name | zip(var_lv_size) | list }}"
In this answer you're using the new way to use loops with keyword loop and using filters like zip and turning the result into a list type for iteration in the loop.

Ansible Undefined variable in vars_files item but the variable was defined using set_fact

I'm trying to select a local file based on what motherboard version exists on the machine on which Ansible will deploy those files.
So my approach was to use dictionary in format {"<board_name>" : "<local_file>.yml"}. And use this dictionary to populate a new variable (pcu_config here) that will finally store the name of the file to be used by ansible. I've gotten so far as shown in my implementation, and I'm get undefined variable error at line
vars_files:
- "{{ playbook_dir }}/pcu_config/{{ pcu_config }}"
But as can be seen in the debug msg in the image of the output just before this play, pcu_config is indeed defined(and is the correct filename too).
- name: Find the pcu_config
hosts: vehicle
gather_facts : no
vars:
pcu_config_dict:
VirtualBox: virtual.yml
n1: n1.yml
n2: n2.yml
n3: n3.yml
n4a: n4a.yml
n4b: n4b.yml
tasks:
- name: Find the motherboard name
shell: cat /sys/devices/virtual/dmi/id/board_name
register: board_name
- name: Find the motherboard version
shell: cat /sys/devices/virtual/dmi/id/chassis_version
register: board_version
- debug:
msg: "board_name : {{ board_name.stdout }}, board_version: {{ board_version.stdout }}, {{pcu_config_dict}} "
- name: Assign the PCU_config file for other than n4's
set_fact:
pcu_config: '{{pcu_config_dict[ board_name.stdout | default("this cpu does not exist in the dict")] | default("") }}'
when: board_name.stdout != "n4"
- name: Assign the PCU_config file for n4
set_fact:
pcu_config: '{{pcu_config_dict[board_version.stdout | default("this cpu_version does not exist in the dict")] | default("") }}'
when: board_name.stdout == "n4"
- name : Check pcu_config is available elsewhere in playbook
hosts: vehicle
tasks:
- debug:
msg: "{{ pcu_config }}, {{ playbook_dir }}"
- name: Deploy software to vehicle
hosts: vehicle
vars_files:
- "{{ playbook_dir }}/pcu_config/{{ pcu_config }}"
- "{{ playbook_dir }}/os_config/deb-files-cache.yml"
- "{{ playbook_dir }}/os_config/python_dep.yml"
roles:
- role: logger_network
tags: base
Here is the output when I run this playbook on my virtual machine.
Suggestions welcome to solve the X or Y problem.(ref #Zeitounator's comment)
Not the best approach but to access the variables set by set_facts elsewhere in the playbook, using cacheable: yes works.
- name: Assign the PCU_config file for other than n4's
set_fact:
pcu_config: '{{pcu_config_dict[ board_name.stdout | default("this cpu does not exist in the dict")] | default("") }}'
cacheable: yes
when: board_name.stdout != "n4"
- name: Assign the PCU_config file for n4
set_fact:
pcu_config: '{{pcu_config_dict[board_version.stdout | default("this cpu_version does not exist in the dict")] | default("") }}'
cacheable: yes
when: board_name.stdout == "n4"

Ansible loop to add member in Azure AD group using graph api

I have been stuck to loop over each "members" in variable file. Reason to take each members is to get user_id and passing those user id in graph api will be able to POST those members in specific AD group.
main.yml
- name: Check if Group Exists
include_tasks: tasks/groups/check_group.yml
- name: Create Group
include_tasks: tasks/groups/create_group.yml
when: item.state == "present" and az_group_id == ""
- name: Populate the group with members
include_tasks: tasks/groups/add_member.yml
when: az_group_id != "" and item.state == "present"
loop: "{{ az_group_config_data }}"
add_member.yml
- name: Get Azure AD User ID
include_tasks: tasks/users/gets/get_user_id.yml
- name: Add Member to a group
uri:
url: "{{ graph_api_base_url }}{{ group_service_context }}/{{ az_group_id }}/members/$ref"
method: POST
body: "{{ lookup('template','./add-group-member-body.j2') }}"
status_code: 204
return_content: yes
use_proxy: no
headers:
Authorization: Bearer {{ token }}
body_format: json
register: group_create_result
get_userid.yml
- name: Register list of Azure Users as Ansible Fact
uri:
url: "{{ graph_api_base_url }}{{ user_service_context }}?$top=999&$select=mail,id"
method: GET
status_code: 200
use_proxy: no
headers:
Authorization: Bearer {{ token }}
register: user_list_result
- name: Register User {{ az_user_mail }} ID as Ansible Fact
set_fact:
az_user_id: "{{ user_list_result.json.value | json_query(az_user_id_query) }}"
defaults/main.yml
az_group_config_data:
- az_group_name: ghe-test-users
aws_account: sbx
environment: poc
state: present
members:
- name#name.com
- name#example.com
- test#test.com
vars/main.yml
az_group_name: "{{ item.az_group_name }}"
az_user_mail: "{{ item.members }}"
# api helpers
graph_api_base_url: "https://graph.microsoft.com/v1.0/"
group_service_context: "groups"
user_service_context: "users"
# query helpers
az_group_id_query: "[?displayName == '{{ az_group_name }}'].id | [0]"
az_user_id_query: "[?mail == '{{ az_user_mail }}'].id | [0]"
add-group-member-body.j2
{
"#odata.id": "https://graph.microsoft.com/v1.0/directoryObjects/{{ az_user_id }}"
}
Below code works to fetch email accounts
- name: latest-debug
set_fact:
az_user_mail: "{{ populate.1 }}"
loop: "{{ az_group_config_data|subelements('members') }}"
loop_control:
loop_var: populate
My issue is only last email account is getting created.

Print json output in Ansible and store in a list?

Ansible playbook:
---
- task:-
- code goes here
- name: Trying to get instance Private IP from ASG
ec2_instance_facts:
instance_ids:
- "{{ item }}"
with_items: "{{ INSTANCE_IDS_FROM_ASG }}"
register: instance_ids_result
- set_fact:
msg: "{{ instance_ids_result | json_query('results[*].instances[*].network_interfaces[*].private_ip_address') }} "
- debug: var=msg
I have the output as follows:
ok: [localhost] => {
"msg": [
[
"172.31.144.74"
],
[
"172.31.147.69"
]
]
}
But, I would need the output in a list as ["172.31.144.74", "172.31.147.69"] or "172.31.147.69" "172.31.147.69".
What is the best way to print it that way?
You could flatten your list using the filter
- set_fact:
msg: "{{ instance_ids_result | json_query('results[*].instances[*].network_interfaces[*].private_ip_address') | flatten }} "

Using ansible launch configuration module ec2_lc and securitygroup names versus id

I want to accomplish the following in aws ec2:
Create security groups using ansible module ec2_group.
Create a launch configuration using ansible module ec2_lc and attach a security group created earlier.
Now, i want to use the security group names instead of id's because i want to be able to recreate the whole infrastructure with ansible if needed.
Recreating security groups will cause the id of the group to be different.
But the ec2_lc module only accepts security group id's.
Is there any way i can map a security group id to a name?
I am defining security groups like this:
- name: create ec2 group
ec2_group:
name: "{{ item.name }}"
description: "{{ item.description }}"
vpc_id: "{{ item.vpc_id }}"
region: "{{ item.region }}"
state: present
rules: "{{ item.rules }}"
rules_egress: "{{ item.rules_egress }}"
register: sg
The launch configuration code looks like this:
- name: Create Launch Configuration
ec2_lc:
region: "{{ item.region }}"
name: "{{ item.name }}"
image_id: "{{ item.image_id }}"
key_name: "{{ item.key_name }}"
security_groups: "{{ item.security_groups }}" # how can i refer to specific group_id based on a group name?
instance_type: "{{ item.instance_type }}"
user_data: "{{ item.ec2_user_data }}"
instance_profile_name: "{{ item.instance_profile_name }}"
assign_public_ip: "{{ item.assign_public_ip }}"
Use the ec2_group-facts to query the security groups by name:
- ec2_group_facts:
filters:
group-name:
- "{{ sg.name }}"
register: ec2sgs
- debug:
msg: "{{ ec2sgs.security_groups | map(attribute='group_id')| list }}"
With some tribute to this question, you can try this:
- name: Create Launch Configuration
ec2_lc:
...
security_groups: "{{ sg.results | selectattr('item.name','equalto',item) | join('',attribute='group_id') }}"
...
You can write a filter, that can make an aws api call for you dynamically.
For instance I have something like this in my vars/main.yml
public_sg_id: "{{ 'Public' |get_sg(public_vpc_id, aws_region) }}"
Here is the code for get_sg filter.
import boto.ec2
from ansible import errors
def get_sg(name, vpc_id, region):
connect = boto.ec2.connect_to_region(region)
filter_by = {
"tag-key": "Name",
"tag-value": name,
"vpc-id": vpc_id
}
sg_groups = connect.get_all_security_groups(filters=filter_by)
if len(sg_groups) == 1:
return sg_groups[0].id
elif len(sg_groups) > 1:
raise errors.AnsibleFilterError(
"Too many results for {0}: {1}".format(
name, ",".join(sg_groups)
)
)
else:
raise errors.AnsibleFilterError(
"Security Group {0} was not found".format(name)
)

Resources