Home > OS >  Create Ansible inventory using Terraform
Create Ansible inventory using Terraform

Time:12-25

I am trying to create Ansible inventory file using local_file function in Terraform (I am open for suggestions to do it in a different way)

module "vm" config:

resource "azurerm_linux_virtual_machine" "vm" {
  for_each                        = { for edit in local.vm : edit.name => edit }
  name                            = each.value.name
  resource_group_name             = var.vm_rg
  location                        = var.vm_location
  size                            = each.value.size
  admin_username                  = var.vm_username
  admin_password                  = var.vm_password
  disable_password_authentication = false
  network_interface_ids           = [azurerm_network_interface.edit_seat_nic[each.key].id]
  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

output "vm_ips" {
  value = toset([
    for vm_ips in azurerm_linux_virtual_machine.vm : vm_ips.private_ip_address
  ])
}

When I run terraform plan with the above configuration I get:

Changes to Outputs:
    test = [
        "10.1.0.4",
    ]

Now, in my main TF I have the configuration for local_file as follows:

resource "local_file" "ansible_inventory" {
  filename = "./ansible_inventory/ansible_inventory.ini"
  content = <<EOF
  [vm]
  ${module.vm.vm_ips}
EOF
}

This returns the error below:

Error: Invalid template interpolation value
on main.tf line 92, in resource "local_file" "ansible_inventory":
90:   content = <<EOF
91:   [vm]
92:   ${module.vm.vm_ips}
93: EOF
module.vm.vm_ips is set of string with 1 element
Cannot include the given value in a string template: string required.

Any suggestion how to inject the list of IPs from the output into the local file while also being able to format the rest of the text in the file?

CodePudding user response:

If you want the Ansible inventory to be statically sourced from a file in INI format, then you basically need to render a template in Terraform to produce the desired output.

module/templates/inventory.tmpl:

[vm]
%{ for ip in ips ~}
${ip}
%{ endfor ~}

alternative suggestion from @mdaniel:

[vm]
${join("\n", ips)}

module/config.tf:

resource "local_file" "ansible_inventory" {
  content = templatefile("${path.module}/templates/inventory.tmpl",
    { ips = module.vm.vm_ips }
  )

  filename        = "${path.module}/ansible_inventory/ansible_inventory.ini"
  file_permission = "0644"
}

A couple of additional notes though:

You can modify your output to be the entire map of objects of exported attributes like:

output "vms" {
  value = azurerm_linux_virtual_machine.vm
}

and then you can access more information about the instances to populate in your inventory. Your templatefile argument would still be the module output, but the for expression(s) in the template would look considerably different depending upon what you want to add.

You can also utilize the YAML or JSON inventory formats for Ansible static inventory. With those, you can then leverage the yamldecode or jsondecode Terraform functions to make the HCL2 data structure transformation much easier. The template file would become a good bit cleaner in that situation for more complex inventories.

  • Related