Skip to content

Commit a60cf3c

Browse files
inventory/aws_ec2 - support jinja2 filters in hostnames option (#2427) (#2456)
This is a backport of PR #2427 as merged into main (995bf94). SUMMARY Closes #2402 ISSUE TYPE Feature Pull Request COMPONENT NAME inventory/aws_ec2 Reviewed-by: Helen Bailey <[email protected]> Reviewed-by: Bikouo Aubin
1 parent 52ad905 commit a60cf3c

File tree

5 files changed

+129
-7
lines changed

5 files changed

+129
-7
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
minor_changes:
3+
- inventory/aws_ec2 - Support jinja2 expression in ``hostnames`` variable(https://github.com/ansible-collections/amazon.aws/issues/2402).

plugins/inventory/aws_ec2.py

+32-7
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
- Can be one of the options specified in U(http://docs.aws.amazon.com/cli/latest/reference/ec2/describe-instances.html#options).
3737
- If value provided does not exist in the above options, it will be used as a literal string.
3838
- To use tags as hostnames use the syntax tag:Name=Value to use the hostname Name_Value, or tag:Name to use the value of the Name tag.
39+
- Jinja2 filters can be added to the hostnames string. Added in version 9.2.0.
3940
type: list
4041
elements: raw
4142
default: []
@@ -268,6 +269,15 @@
268269
- us-east-1
269270
hostvars_prefix: 'aws_'
270271
hostvars_suffix: '_ec2'
272+
273+
---
274+
275+
# Define hostnames variables with jinja2 filters.
276+
plugin: amazon.aws.aws_ec2
277+
regions:
278+
- us-east-1
279+
hostnames:
280+
- "tag:Name | replace('test', 'prod')"
271281
"""
272282

273283
import re
@@ -549,6 +559,26 @@ def _sanitize_hostname(self, hostname):
549559
else:
550560
return to_text(hostname)
551561

562+
def _get_hostname_with_jinja2_filter(self, instance, preference, return_single_hostname=False):
563+
jinja2_filter = None
564+
is_template = False
565+
if "|" in preference:
566+
preference, jinja2_filter = preference.split("|", maxsplit=1)
567+
preference = preference.rstrip()
568+
is_template = True
569+
if preference.startswith("tag:"):
570+
hostname = _get_tag_hostname(preference, instance)
571+
else:
572+
hostname = _get_boto_attr_chain(preference, instance)
573+
if is_template:
574+
template_var = "{{'%s'|%s}}" % (hostname, jinja2_filter)
575+
if isinstance(hostname, list):
576+
template_var = "{{%s|%s}}" % (hostname, jinja2_filter)
577+
hostname = self.templar.template(variable=template_var, disable_lookups=False)
578+
if isinstance(hostname, list) and return_single_hostname:
579+
hostname = hostname[0] if hostname else None
580+
return hostname
581+
552582
def _get_preferred_hostname(self, instance, hostnames):
553583
"""
554584
:param instance: an instance dict returned by boto3 ec2 describe_instances()
@@ -570,11 +600,8 @@ def _get_preferred_hostname(self, instance, hostnames):
570600
separator = preference.get("separator", "_")
571601
if hostname and hostname_from_prefix and "prefix" in preference:
572602
hostname = hostname_from_prefix + separator + hostname
573-
elif preference.startswith("tag:"):
574-
tags = _get_tag_hostname(preference, instance)
575-
hostname = tags[0] if tags else None
576603
else:
577-
hostname = _get_boto_attr_chain(preference, instance)
604+
hostname = self._get_hostname_with_jinja2_filter(instance, preference, return_single_hostname=True)
578605
if hostname:
579606
break
580607
if hostname:
@@ -602,10 +629,8 @@ def _get_all_hostnames(self, instance, hostnames):
602629
separator = preference.get("separator", "_")
603630
if hostname and hostname_from_prefix and "prefix" in preference:
604631
hostname = hostname_from_prefix[0] + separator + hostname[0]
605-
elif preference.startswith("tag:"):
606-
hostname = _get_tag_hostname(preference, instance)
607632
else:
608-
hostname = _get_boto_attr_chain(preference, instance)
633+
hostname = self._get_hostname_with_jinja2_filter(instance, preference)
609634

610635
if hostname:
611636
if isinstance(hostname, list):
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
- hosts: 127.0.0.1
3+
connection: local
4+
gather_facts: false
5+
environment: "{{ ansible_test.environment }}"
6+
tasks:
7+
- module_defaults:
8+
group/aws:
9+
access_key: "{{ aws_access_key }}"
10+
secret_key: "{{ aws_secret_key }}"
11+
session_token: "{{ security_token | default(omit) }}"
12+
region: "{{ aws_region }}"
13+
block:
14+
# Create VPC, subnet, security group, and find image_id to create instance
15+
- name: Setup EC2 network
16+
ansible.builtin.include_tasks: tasks/setup.yml
17+
18+
- name: Create a new host
19+
amazon.aws.ec2_instance:
20+
image_id: "{{ image_id }}"
21+
name: "{{ resource_prefix }}"
22+
tags:
23+
Tag1: tag1.test-ansible
24+
Tag2: tag2.test-ansible
25+
purge_tags: true
26+
instance_type: t2.micro
27+
security_groups: "{{ sg_id }}"
28+
vpc_subnet_id: "{{ subnet_id }}"
29+
wait: false
30+
register: setup_instance
31+
32+
# refresh inventory
33+
- ansible.builtin.meta: refresh_inventory
34+
35+
- name: Display ansible hostvars variable
36+
ansible.builtin.debug:
37+
var: hostvars
38+
39+
- name: Assert that hostvars contain multiple hostnames (hostnames with multiple tags and allow_duplicated_hosts=true)
40+
ansible.builtin.assert:
41+
that:
42+
- hostvars.keys() | length == 2
43+
- '"tag1.prod-Ansible" in hostvars'
44+
- '"tag2.prod-Ansible" in hostvars'
45+
when:
46+
- search_multiple_tags | default(false) | bool
47+
- (allow_duplicated_hosts | default(false) | bool)
48+
49+
- name: Assert that hostvars contain only 1 hostname (hostnames with multiple tags and allow_duplicated_hosts=false)
50+
ansible.builtin.assert:
51+
that:
52+
- hostvars.keys() | length == 1
53+
- '"tag1.prod-Ansible" in hostvars'
54+
when:
55+
- search_multiple_tags | default(false) | bool
56+
- not (allow_duplicated_hosts | default(false) | bool)
57+
58+
- name: Assert that hostvars contain only 1 hostname (hostnames with single tag)
59+
ansible.builtin.assert:
60+
that:
61+
- hostvars.keys() | length == 1
62+
- '"TAG1.PROD-ANSIBLE" in hostvars'
63+
when:
64+
- not (search_multiple_tags | default(false) | bool)
65+
- not (allow_duplicated_hosts | default(false) | bool)

tests/integration/targets/inventory_aws_ec2/runme.sh

+12
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,18 @@ ansible-playbook playbooks/test_inventory_cache.yml "$@"
8181
ansible-playbook playbooks/create_inventory_config.yml -e "template='inventory_with_ssm.yml.j2'" "$@"
8282
ansible-playbook playbooks/test_inventory_ssm.yml "$@"
8383

84+
# generate inventory config with hostnames containing multiple tags and jinja2 filters (allow_duplicated_hosts=False)
85+
ansible-playbook playbooks/create_inventory_config.yml -e "template='inventory_with_hostnames_with_jinja2_filters.yml.j2'" -e "search_multiple_tags=true" "$@"
86+
ansible-playbook playbooks/test_populating_inventory_with_hostnames_with_jinja2_filters.yml -e "search_multiple_tags=true" "$@"
87+
88+
# generate inventory config with hostnames containing multiple tags and jinja2 filters (allow_duplicated_hosts=True)
89+
ansible-playbook playbooks/create_inventory_config.yml -e "template='inventory_with_hostnames_with_jinja2_filters.yml.j2'" -e "allow_duplicated_hosts=true" -e "search_multiple_tags=true" "$@"
90+
ansible-playbook playbooks/test_populating_inventory_with_hostnames_with_jinja2_filters.yml -e "allow_duplicated_hosts=true" -e "search_multiple_tags=true" "$@"
91+
92+
# generate inventory config with hostnames containing single tag and jinja2 filters
93+
ansible-playbook playbooks/create_inventory_config.yml -e "template='inventory_with_hostnames_with_jinja2_filters.yml.j2'" "$@"
94+
ansible-playbook playbooks/test_populating_inventory_with_hostnames_with_jinja2_filters.yml "$@"
95+
8496
# remove inventory cache
8597
rm -r aws_ec2_cache_dir/
8698

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
plugin: amazon.aws.aws_ec2
2+
access_key: '{{ aws_access_key }}'
3+
secret_key: '{{ aws_secret_key }}'
4+
{% if security_token | default(false) %}
5+
session_token: '{{ security_token }}'
6+
{% endif %}
7+
{% if allow_duplicated_hosts | default(false) %}
8+
allow_duplicated_hosts: True
9+
{% endif %}
10+
regions:
11+
- '{{ aws_region }}'
12+
hostnames:
13+
{% if search_multiple_tags | default(false) %}
14+
- "tag:Tag1,Tag2 | replace('test', 'prod') | title()"
15+
{% else %}
16+
- "tag:Tag1 | replace('test', 'prod') | upper()"
17+
{% endif %}

0 commit comments

Comments
 (0)