Search code examples
ansiblejinja2ansible-inventoryservicenow

How to set host variable for ansible dynamic inventories from servicenow?


I'm playing with ansible dynamics inventories using servicenow.servicenow.now plugin. The main idea, is to tell ansible to use winrm kerberos connection type to all host I pull from it. So i'm using the compose option to do that, but, it seems the string are not parsed as expected. It says in the doc that it should be jinja2 expression. In my case I just want to set a string with mixed number and letters. For some reason it only works with numbers?

here is the plugin configuration:

# Simple Inventory Plugin example
plugin: servicenow.servicenow.now
instance: myamazingcompany
username: username
password: 'amazingpassword'
filter_results: 'install_status=5'
fields: [name,support_group]
table: computers
use_extra_vars: yes
keyed_groups:
  - key: sn_support_group | lower
    prefix: ''
    separator: ''
compose:
  ansible_user: "my_windows_user"
  ansible_password: "4567"
  connection: "winrm"
  ansible_winrm_transport: "kerberos"
  ansible_winrm_server_cert_validation: "ignore"

Here is the result I get from servicenow

[...]
           "windows_server_1": {
                "ansible_password": "4567",
                "sn_name": "windows_server_1",
                "sn_support_group": "windows_support"
            },
            "windows_server_2": {
                "ansible_password": "4567",
                "sn_name": "windows_server_2",
                "sn_support_group": "windows_support"
            },
[...]

Why do I only get the ansible_password variable set to my hosts ? I have tried using simple quote, no quote at all and it does not work. I'm confused as the plugin documentation does not explain anything.

Thanks for your help


Solution

  • Yeah, this comes up a lot and your experience is made worse by them just silently swallowing jinja2 errors during that compose: step

    So, first, the answer to your question, and then a suggestion how one can side-step this silliness in the future

    The answer is because because each of those compose values are implicitly wrapped in {{ }} which is why they talk about "jinja2 expressions" and is the reason why this works at all in their docs:

    compose:
      sn_tags: sn_sys_tags.replace(" ", "").split(',')
      ansible_host: sn_ip_address
    

    one can see that both of those are python jinja2 and for sure not keys in the resulting json; thus, what's actually going on here is:

    compose:
      sn_tags: '{{ sn_sys_tags.replace(" ", "").split(",") }}'
      ansible_host: '{{ sn_ip_address }}'
    

    but I guess including the mustaches in the .yaml file would lead a user to think they can use mustaches anywhere which is for sure untrue

    Thus, in your circumstance, you have to make those keys into expressions that produce those literals, like so:

    
    compose:
      ansible_user: '"my_windows_user"'
      ansible_password: '"4567"'
      connection: '"winrm"'
      ansible_winrm_transport: '"kerberos"'
      ansible_winrm_server_cert_validation: '"ignore"'
    

    since {{ winrm }} is a jinja2 error if there is no such symbol in scope, but {{ "winrm" }} is the string winrm; the outer '" is to escape those " from yaml

    The reason why your password: "1234" worked is because {{ 1234 }} resolves to itself, in ways that {{ foobar }} does not, although pedantically ansible_password: was then int and not str but I guess it didn't get far enough for that explosion to manifest :-)


    Now, I was trying to answer the question you asked, but in the future, what you're really after are group_vars since every one of those keys you provided do not vary based on the inventory response, and thus they belong in a group_vars/${whatever}.yaml file, even if the filename ends up being all.yaml because it applies equally to every single host in the inventory