Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating ansible inventory for multiple target hosts

I am using terraform to create resources then using templatefile approach to create ansible inventory in yaml language. I am creating multiple Virtual Machines in terraform and configure them using ansible in single pipeline.

Issue I am having is yaml file uses - for lists which is giving error in ansible playbook

Outputs.tf

resource "local_file" "AnsibleInventory" {
content = templatefile("inventory.tmpl",
{
ansible_port = "5986"
ansible_connection = "winrm"
ansible_winrm_server_cert_validation = "ignore"
ansible_winrm_transport = "ntlm"
vm-ip = data.azurerm_public_ip.main.*.ip_address,
username = "testadmin",
ansible_password = "abc"
}
)
filename = "inventory.json"
}

inventory.tmpl

${jsonencode({
  "all": {
    "hosts": {
      "server": [
        for ip in vm-ip : { 
        "ansible_host": "${ip}",
        "ansible_port": 5986,
        "ansible_user": "testadmin",
        "ansible_winrm_transport": "ntlm",
        "ansible_connection": "winrm",
        "ansible_winrm_server_cert_validation": "ignore",
        "ansible_password": "abc"
        }
      ]
    }
  }
})}

inventory.json

{"all":{"hosts":{"server":{"ansible_connection":"winrm","ansible_host":"343434","ansible_password":"abc","ansible_port":5986,"ansible_user":"testadmin","ansible_winrm_server_cert_validation":"ignore","ansible_winrm_transport":"ntlm"}}}}

inventory.yaml

all:
  hosts:
    server:
    - ansible_connection: winrm
      ansible_host: 40.88.14.205
      ansible_password: abc
      ansible_port: 5986
      ansible_user: testadmin
      ansible_winrm_server_cert_validation: ignore
      ansible_winrm_transport: ntlm

Is my approach right then how to remove "-" from code or am I doing something wrong.

Edited:

I am converting json file to yaml file using bash in pipeline.

python -c 'import sys, yaml, json; yaml.safe_dump(json.load(sys.stdin), sys.stdout, default_flow_style=False)' < inventory.json > inventory.yaml
like image 539
bhupinder Avatar asked Jun 17 '20 13:06

bhupinder


People also ask

How do I pass multiple hosts in Ansible?

If you have equal count of hosts in each group, you can use pattern + serial. Ansible forms host list by pattern moving through groups sequentially. So if you have equal count of hosts, then batches formed by serial will be equal to groups.

Can Ansible have multiple inventory files?

Ansible works against multiple systems in your infrastructure at the same time. It does this by selecting portions of systems listed in Ansible's inventory, which defaults to being saved in the location /etc/ansible/hosts . You can specify a different inventory file using the -i <path> option on the command line.


1 Answers

As pointed out by @ydaetskcoR's comment, your inventory is wrong because you need the list of hosts to be an immediate child of the hosts attribute.


Note that the list you end up with is produced by the use of the brackets [], producing tuples, around for in your Terraform template as opposed to the use of the bracket {}, producing objects.

The type of brackets around the for expression decide what type of result it produces. The above example uses [ and ], which produces a tuple. If { and } are used instead, the result is an object, and two result expressions must be provided separated by the => symbol.

https://www.terraform.io/docs/configuration/expressions.html#for-expressions


If your structure, should look like all > server1, server2, server3

Then your inventory should be

all:
  hosts:
    server1:
      ansible_host: 10.0.0.2
      ansible_port: 5986
    server2:
      ansible_host: 10.0.0.3
      ansible_port: 5986
    server3:
      ansible_host: 10.0.0.4
      ansible_port: 5986

This is an approach for a Terraform template producing this:

${yamlencode({
  "all": {
    "hosts": {
      for i, ip in vm-ip:
        "server${i+1}" => {
          "ansible_host": "${ip}",
          "ansible_port": 5986
        }
    }
  }
})}

Which gives this valid inventory.yaml:

"all":
  "hosts":
    "server1":
      "ansible_host": "10.0.0.2"
      "ansible_port": 5986
    "server2":
      "ansible_host": "10.0.0.3"
      "ansible_port": 5986
    "server3":
      "ansible_host": "10.0.0.4"
      "ansible_port": 5986

On the other hand, if you want a structure like all > server > server1, server2, server3

Then you should make server a child of all and use the children entry in your inventory:

all:
  children:
    server:
      hosts:
        server1:
          ansible_host: 10.0.0.2
          ansible_port: 5986
        server2:
          ansible_host: 10.0.0.3
          ansible_port: 5986
        server3:
          ansible_host: 10.0.0.4
          ansible_port: 5986

For this one, the corresponding Terraform template would be

${yamlencode({
  "all": {
    "children": {
      "server": {
        "hosts": {
          for i, ip in vm-ip:
            "server${i+1}" => {
              "ansible_host": "${ip}",
              "ansible_port": 5986
            }
        }
      }
    }
  }
})}

Which produce this inventory.yaml:

"all":
  "children":
    "server":
      "hosts":
        "server1":
          "ansible_host": "10.0.0.2"
          "ansible_port": 5986
        "server2":
          "ansible_host": "10.0.0.3"
          "ansible_port": 5986
        "server3":
          "ansible_host": "10.0.0.4"
          "ansible_port": 5986

Note: for all the examples above, I am using the terraform file test.tf below:

resource "local_file" "AnsibleInventory" {
  content = templatefile("inventory.tpl", {
    vm-ip = ["10.0.0.2","10.0.0.3","10.0.0.4"],
  })
  filename = "inventory.yaml"
}
like image 89
β.εηοιτ.βε Avatar answered Nov 15 '22 11:11

β.εηοιτ.βε