Fail to use yaml reference in ansible inventory plugin - ansible

I would like to use this config with an inventory plugin
# test_inventory_xxx.yml
plugin: cloudscale # or openstack or ...
inventory_hostname: &inventory_hostname_value uuid
compose:
setting_of_inventory_hostname: *inventory_hostname_value
I get no error, but the value is not set. And it is valid yaml. (At least my checker nor myself see an error.
So I decided to simplify it by using the constructed plugin, which is standard:
# inventory_constructed.yaml
plugin: constructed
# add variables to existing inventory
keyed_groups:
- key: from_inventory
prefix: inventory
parent_group: &common_parent_group test_group_1
compose:
var_from_constructed: 1233456789
also_from_constr: "'also'" # must be in quotes 2x!
some_from_constr: &ref1 1234567777
ref_from_constr: *ref1 # this works fine
ref_to_test: *common_parent_group # <--- this line returns an error
strict: yes
Now I get the error: Could not set ref_to_test for host my_host: 'test_group_1' is undefined
But it passes when I uncomment the marked line. (the ref &common_parent_group is still defined, but not used with *common_parent_group.) Why is test_group_1 undefined in one case, but not in the other?
How to reproduce: ansible -i some_of/your_inventory -i inventory_constructed.yaml -m debug -a var=vars
What do I do wrong? Or what else is the problem?
(I tought it is an missing feature, so original info in https://github.com/ansible/ansible/issues/69043)

It seems like parent_group takes a literal string while ref_to_test takes a Jinja2 expression (because it's under compose). It should fail the same way if you write
ref_to_test: test_group_1
because test_group_1 simply isn't a Jinja2 variable. You'll have to write
ref_to_test: "'test_group_1'"
just like above so Jinja2 sees 'test_group_1' which is a literal string. This also means you can't use an alias because parent_group does not evaluate its content with Jinja2 and therefore shouldn't include quotes in its content.

Related

Ansible Inventory plugins: the verify_file method of InventoryModule base class receives the input string modified with a path-like prefix

I have implemented an Inventory plugin according to the guide on developing dynamic inventory.
On invoking my plugin via the CLI, I am experiencing an issue with the way the inventory source is handled from the command line to the verify_file() method. In particular, I am passing a simple string to the command line but the verify_file() method is observed to receive not just that string itself, but actually a concatenation of the string plus my user home path (i.e. the path that is equivalent to "~"). For example, an input of "input_string" from the command line will provide "/home/{user}/input_string" to the verify_file() method.
This behaviour is unexpected since the built-in host_list inventory plugin, like my custom plugin, expects a simple string (not a path) and, also, does seem to omit any logic to handle any expectation of receiving such a path-like input.
Clearly I could make my plugin work by adding some additional logic to handle what is occurring. But I would like to understand if/how I can actually avoid the modification of the input from happening at all - like it seems to be working already for the built-in host_list plugin.
Here is the command I am issuing to the CLI:
ansible-inventory -i 'myUniqueInputString' --list --output ansible-inventory -vvvv
Here is an excerpt of a minimal repro:
class InventoryModule(BaseInventoryPlugin):
NAME = 'my_plugin'
def __init__(self):
super(InventoryModule, self).__init__()
self.foo_stuff = {}
def verify_file(self, input_string):
"""check the input value is same as was passed via the CLI."""
valid = False
b_path = to_bytes(input_string, errors='surrogate_or_strict')
if not os.path.exists(b_path) and ',' not in input_string:
try:
assert input_string == "myUniqueInputString"
valid = True
except AssertionError:
print(f"Received this: {input_string} but was expecting this: myUniqueInputString")
return valid
These is the relevant line from the stdout:
Received this: /home/testuser/myUniqueInputString but was expecting this: myUniqueInputString
The key evidence is the input string of "myUniqueInputString" providing "/home/testuser/myUniqueInputString" to the function. How to stop this modification of the input?

google deployment manager access nested properties with --properties argument

If Im using a jinja template I can override properties via command line like this:
--properties zone:us-central1-a,machineType:n1-standard-1,image:debian-9
But I see no documentation or examples for doing this with nested properties like labels or environmentVariables for example:
resources:
- name: resource-name
type: 'gcp-types/cloudfunctions-v1:projects.locations.functions'
properties:
labels:
testlabel1: testlabel1value
testlabel2: testlabel2value
environmentVariables:
TEST: 'zzzzzzzzz'
How do set properties like these? this does not work: --properties labels:testlabel1:newvalue
The short answer here is that the --properties flag is not meant to pass property values to the template. A template cannot run without a configuration file, the --properties flag is meant to replace the config file. Each parameter you pass is the same as listing them in a config file.
Essentially using --template my-template.py --properties zone:us-central1-f is the equivalent of running --config myConfig.yaml where the YAML is defined as such:
imports:
- path: my-template.py
resources:
- name: some-resource
type: my-temaplte.py
properties:
zone: us-central1-f
The --properties flag is not meant to pass raw data to replace non-variables.
Although this does not directly answer your question, you shouldn't normally need to define nested values in the flag. Your template will generally call on direct variables taken from the object properties.
despite this, I did try some tests, and as far as I can tell, you can't do this.
After some trial and error I managed to pass an object via command line like this:
--properties ^~^labels:{'testlabel1: testlabel1value','testlabel2: testlabel2value'}~environmentVariables:{'TEST: zzzzzzzzz'}
This sequence of symbols ^~^ is how you can change delimiter. You have to put it at the beginning of your properties. More info about escaping you can find here.
I put single apostrophe over single key value pair because we need that space between key and value. Otherwise it's interpereted as key with null value.
If you're using Bash shell you should also escape {,} symbols.

Is there any way to check a Yml/Yaml file is valid ansible Playbook?

It should check all cases of imports, includes and custom variables. As of now I can see that ansible-playbook playbook.yml --list-tasks fails in few cases when we have custom variables. For example a yml having tasks only should not be a valid play. yml importing those tasks should be treated as valid ansible play.
Because of the way templating etc works, the only way to know for sure beyond basic syntax checking (eg --syntax-check or --list-tasks) is to execute it. --check-mode can tell you some things if your playbook is written correctly to support it, and there are other tools around like ansible-lint that might help, but nothing short of executing the playbook will tell you 100%.

Ansible nested variables in inventory

I've been using Ansible for a while now, and in general have no trouble with variables in inventories. However, for the first time I have to override a nested variable in the inventory, and the I'd expect it to work... doesn't.
The default/main.yml of the role looks like this:
archiver_config:
archiver_folder: "/opt/archiver"
source_folder: "/var/tmp/images"
archive_folder: "/var/tmp/imagearchive"
min_diskspace: 1e6
logfile: "/var/log/archiver.log"
I need to override the default archive folder for some hosts because some of them have an external filesystem attached for this purpose, so I did this in the inventory:
[tdevices]
10.8.0.38 adeploy_name=16014c archiver_config.archive_folder=/media/ext
I have also tried putting the value in double and single quotes, like e.g.
archiver_config.archive_folder='/media/ext'
But it doesn't work. Ansible doesn't throw any errors, but the default value does not get overridden. What's the correct syntax to do this?
There are no "nested variables" in your example. There is only one variable archiver_config which is a dictionary (hash).
You cannot assign a value to a dictionary key in the inventory file.
What you can do is add a variable in the defaults/main.yml, use it as a value for the key (now, this can be called a nested variable):
archive_folder: "/var/tmp/imagearchive"
archiver_config:
archiver_folder: "/opt/archiver"
source_folder: "/var/tmp/images"
archive_folder: "{{archive_folder}}"
min_diskspace: 1e6
logfile: "/var/log/archiver.log"
and assign value to it in the inventory file:
[tdevices]
10.8.0.38 adeploy_name=16014c archive_folder=/media/ext

How to concatenate several strings and variables in Yaml?

I need to do something like this:
domain: &domain example.com
ip: &ip 1.2.3.4
Address4: v=spf1 include:*domain ip4:*ip -all
I tried this but didn't work, it says there's a syntax error:
Address4: 'v=spf1 include:'*domain' ip4:'*ip' -all'
The implementation (I'm using these files in Ansible) seems to support concatenation, for example this works fine:
Address2: http://*domain # actually this doesn't work, I don't remember the exact example
Any ideas?
I don't exactly get why you're trying to concatinate in YAML, since Ansible uses jinja2 as a templating engine:
Defining variables in a vars file for Ansible (in YAML):
domain: example.com
ip: 1.2.3.4
Referencing variables (still in YAML):
Address4: v=spf1 include:{{ domain }} ip4:{{ ip }} -all
When you use the Address4 variable in the template module (I assume), it works the same way.

Resources