Generating/Executing Terraform Plans Using Ansible
Recently I have been working on a little project of my own based on provisioning a vSphere environment using Ansible as the primary automation tool. My intention of this project was to use IaC as the main driver.
NOTE: You can view this project on GitHub. ansible-vsphere-management.
As part of this project involved provisioning numerous Core Services VMs to provide services such as:
- DNS
- DHCP
- IPAM
- Active Directory (Samba based AD)
The above Core Services are provisioned using Ansible to drive PowerCLI scripts for the most part. I took this route because I found that the vSphere Ansible modules were not flexible enough for my needs. However, once the Core Services were deployed. I wanted to take an easier approach to provisioning additional VMs and etc. This is where Terraform comes into play. Terraform is a great tool for defining your infrastructure as code, provisioning the infrastructure and also provides the ability to tear it all down. However, being that this project is based on IaC, I needed a way to leverage variable definitions already present in the project to also drive the Terraform provisioning. So how did I accomplish this? Quite easily actually. I took the foundation of what Terraform files are required to define the infrastructure that I wanted to provision and used Ansible to generate those configuration files by using Jinja2 templates. By taking this approach it has proven to be a very dynamic way to build out my infrastucture. With all of this being said, I will be outlining with examples on how I was able to accomplish all of this. Hopefully, this post will be beneficial to others as well.
Terraform Variables
Leveraging Existing group_vars To Define Terraform Variables
Here we will be defining our Terraform variables.tf
file. Because all
of the variables are already defined throughout the project we can
leverage those and use a Jinja2 template to generate our variables.tf
file.
So for example, in our group_vars/all/all.yml
Ansible variables we
have some of the following defined:
vsphere_dns_servers:
- "{{ vsphere_dnsdist_vm_ips[0] }}"
- "{{ vsphere_dnsdist_vm_ips[1] }}"
vsphere_dvswitches:
- name: dvSwitch0
active_uplinks:
- dvUplink1
- dvUplink2
- dvUplink3
- dvUplink4
datacenter: "{{ vsphere_vcenter_datacenter['name'] }}"
discovery_protocol:
status: enabled
type: LLDP
operation: both
hosts: "{{ groups['vsphere_hosts'] }}"
load_balancing_policy: LoadBalanceSrcId
mtu: 1500
nics:
- vmnic2
- vmnic3
- vmnic4
- vmnic5
port_groups:
- name: VDS-VLAN-101
num_ports: 128
state: present
type: earlyBinding
vlan_id: 101
- name: VDS-VLAN-102
num_ports: 128
state: present
type: earlyBinding
vlan_id: 102
- name: VDS-VLAN-201
num_ports: 128
state: present
type: earlyBinding
vlan_id: 201
standby_uplinks:
[]
# - dvUplink3
# - dvUplink4
state: present
unused_uplinks:
[]
# - dvUplink4
uplink_ports: 4
vsphere_linux_vapp_template_name: ubuntu-16.04-template
pdns_api_vip: "{{ vsphere_lb_vips[0] }}"
pdns_webserver_port: 8081
vsphere_pri_domain_name: lab.etsbv.internal
Terraform VMs
We also have our terraform_vms
defined as below which is what we will
be using to also generate our VM naming and etc. within our
variables.tf
file. The terraform_vms
definition will also be used
for Terraform VM Resources.
group_vars/all/terraform_vms.yml
:
---
# If defining addl_disk, size is in GB
terraform_vms:
- group: docker_lbs
count: 2
inventory_parent_folder: Docker
memory_mb: 512
naming: docker-lb
vcpu: 1
- group: docker_storage
addl_disk:
- size: 100
count: 2
inventory_parent_folder: Docker
memory_mb: 512
naming: docker-storage
vcpu: 1
- group: docker_swarm_managers
count: 3
inventory_parent_folder: Docker
memory_mb: 1024
naming: docker-mgr
vcpu: 1
- group: docker_swarm_workers
count: 4
inventory_parent_folder: Docker
memory_mb: 4096
naming: docker-wrk
vcpu: 1
Now let’s break down the terraform_vms
variable a bit to understand
what is going on here and what is being defined.
group
: Defines the Ansible
group as well as the Terraform
vsphere_virtual_machine
resource name which we will touch on further
down when we get to Terraform VM Resources.
count
: Defines the number of VMs which should be generated for each
group.
inventory_parent_folder
: Defines the vCenter
folder where the VMs
will be located when provisioned.
memory_mb
: Defines the amount of memory in MB to allocate each VM
within the group.
naming
: Defines the VM naming scheme to use when generating the VMs.
vcpu
: Defines the number of CPUs to allocate to each VM within the
group.
Terraform Variables Jinja2 Template
The terraform_variables.tf.j2
template below is what we will use for
generating our variables.tf
file based on the variables defined in
Leveraging Existing group_vars To Define Terraform
Variables:
variable "dns_servers" {
default = [
{% for dns_server in vsphere_dns_servers %}
"{{ dns_server }}",
{% endfor %}
]
}
variable "dvSwitches" {
default = [
"{{ vsphere_dvswitches[0]['name'] }}",
]
}
variable "esxi_hosts" {
default = [
{% for host in groups['vsphere_hosts'] %}
"{{ host }}",
{% endfor %}
]
}
variable "linux_vm_template" {
default = "{{ vsphere_linux_vapp_template_name }}"
}
variable "pdns_api_key" {}
variable "pdns_server_url" {
default = "http://{{ pdns_api_vip }}:{{ pdns_webserver_port }}/api/v1"
}
variable "pri_domain_name" {
default = "{{ vsphere_pri_domain_name }}"
}
{% for vms in terraform_vms %}
variable "vms_{{ vms['group'] }}" {
default = [
{% for vm in range(vms['count']) %}
{% if loop.index < 10 %}
"{{ vms['naming'] }}-0{{ loop.index }}.{{ vsphere_pri_domain_name }}",
{% elif loop.index >= 10 %}
"{{ vms['naming'] }}-{{ loop.index }}.{{ vsphere_pri_domain_name }}",
{% endif %}
{% endfor %}
]
}
{% endfor %}
variable "vsphere_datacenter" {
default = "{{ vsphere_vcenter_datacenter['name'] }}"
}
variable "vsphere_datacenter_cluster" {
default = "{{ vsphere_vcenter_cluster['name'] }}"
}
variable "vsphere_default_vm_datastore" {
default = "{{ vsphere_vm_services_datastore }}"
}
variable "vsphere_default_vm_network" {
default = "{{ vsphere_vm_services_vswitch }}"
}
variable "vsphere_host_license_key" {}
variable "vsphere_networks" {
default = [
{% for switch in vsphere_dvswitches %}
{% for net in switch['port_groups'] %}
"{{ net['name'] }}",
{% endfor %}
{% endfor %}
]
}
variable "vsphere_password" {}
variable "vsphere_server" {
default = "{{ vsphere_vcsa_network_ip }}"
}
variable "vsphere_username" {}
variable "vsphere_vcenter_license_key" {}
Generated variables.tf
And if we were to generate our variables.tf
based on the above
variables and Jinja2 template it would look similar to below:
variable "dns_servers" {
default = [
"10.0.102.40",
"10.0.102.41",
]
}
variable "dvSwitches" {
default = [
"dvSwitch0",
]
}
variable "esxi_hosts" {
default = [
"esxi-01.lab.etsbv.internal",
"esxi-02.lab.etsbv.internal",
]
}
variable "linux_vm_template" {
default = "ubuntu-16.04-template"
}
variable "pdns_api_key" {}
variable "pdns_server_url" {
default = "http://10.0.102.100:8081/api/v1"
}
variable "pri_domain_name" {
default = "lab.etsbv.internal"
}
variable "vms_docker_lbs" {
default = [
"docker-lb-01.lab.etsbv.internal",
"docker-lb-02.lab.etsbv.internal",
]
}
variable "vms_docker_storage" {
default = [
"docker-storage-01.lab.etsbv.internal",
"docker-storage-02.lab.etsbv.internal",
]
}
variable "vms_docker_swarm_managers" {
default = [
"docker-mgr-01.lab.etsbv.internal",
"docker-mgr-02.lab.etsbv.internal",
"docker-mgr-03.lab.etsbv.internal",
]
}
variable "vms_docker_swarm_workers" {
default = [
"docker-wrk-01.lab.etsbv.internal",
"docker-wrk-02.lab.etsbv.internal",
"docker-wrk-03.lab.etsbv.internal",
"docker-wrk-04.lab.etsbv.internal",
]
}
variable "vsphere_datacenter" {
default = "LAB"
}
variable "vsphere_datacenter_cluster" {
default = "LAB-Cluster"
}
variable "vsphere_default_vm_datastore" {
default = "vSphere_iSCSI_Datastore_1"
}
variable "vsphere_default_vm_network" {
default = "VSS-VLAN-102"
}
variable "vsphere_host_license_key" {}
variable "vsphere_networks" {
default = [
"VDS-VLAN-101",
"VDS-VLAN-102",
"VDS-VLAN-201",
]
}
variable "vsphere_password" {}
variable "vsphere_server" {
default = "10.0.102.60"
}
variable "vsphere_username" {}
variable "vsphere_vcenter_license_key" {}
So as you can see we easily leveraged existing Ansible defined variables
along with our Jinja2 template to define our Terraform variables.tf
file. And remember why this is important and useful. We now have a
streamlined methodology to not only provision out our infrastructure
using Ansible but also to define our Terraform provisioning
consistently.
Terraform Secret Variables
We now also need to maintain some secret variables which we would never
want to keep in our version control system. And again we can use a
Jinja2 template to generate out these super secret variables as well.
For example, the Jinja2 template below could be used to generate our
terraform.tfvars
config:
terraform.tfvars.j2
:
pdns_api_key = "{{ pdns_webserver_password }}"
vsphere_host_license_key = ""
vsphere_password = "{{ vsphere_vcsa_sso_user_info['password'] }}"
vsphere_username = "{{ vsphere_vcsa_sso_user_info['username'] }}"
vsphere_vcenter_license_key = ""
Which would produce the following terraform.tfvars
:
pdns_api_key = "changeme"
vsphere_host_license_key = ""
vsphere_password = "VMw@re1!"
vsphere_username = "[email protected]"
vsphere_vcenter_license_key = ""
Terraform Data Sources
We now will use a standard data_sources.tf
file which will not be
generated using Ansible. However, it will be configured to leverage our
variables.tf
file which we already generated. Below is an example of
what our data_sources.tf
might look like.
data "vsphere_datacenter" "dc" {
name = "${var.vsphere_datacenter}"
}
data "vsphere_host" "esxi_hosts" {
count = "${length(var.esxi_hosts)}"
name = "${var.esxi_hosts[count.index]}"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
data "vsphere_distributed_virtual_switch" "dvs" {
count = "${length(var.dvSwitches)}"
name = "${var.dvSwitches[count.index]}"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
data "vsphere_network" "net" {
count = "${length(var.vsphere_networks)}"
name = "${var.vsphere_networks[count.index]}"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
As you can see from above, we are leveraging pre defined variables.
Terraform Providers
We are only focusing on the vsphere
provider as part of this example
so we actually will use a static vsphere_profider.tf
config which
would look something like:
provider "vsphere" {
user = "${var.vsphere_username}"
password = "${var.vsphere_password}"
vsphere_server = "${var.vsphere_server}"
# if you have a self-signed cert
allow_unverified_ssl = true
}
Terraform Inventory Resources
For our Terraform inventory resources we will be using yet another
Jinja2 template that will be generated for us. This file will define the
vCenter
folder(s) for our VMs to be defined within. If you remember in
the Terraform VMs section we discussed what the
inventory_parent_folder
was defined for. Well here is the Jinja2
template that will define our inventory resources.
resource "vsphere_folder" "terraform_deployed" {
path = "Terraform Deployed"
type = "vm"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
{% set _folders = [] %}
{% for folder in terraform_vms %}
{% if folder['inventory_parent_folder'] is defined %}
{% set _folders = _folders.append(folder['inventory_parent_folder']) %}
{% endif %}
{% endfor %}
{% set folders = _folders|unique|list %}
{% for folder in folders %}
resource "vsphere_folder" "{{ folder }}" {
path = "${vsphere_folder.terraform_deployed.path}/{{ folder }}"
type = "vm"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
{% endfor %}
{% for folder in terraform_vms %}
{% if folder['inventory_parent_folder'] is not defined %}
resource "vsphere_folder" "{{ folder['group'] }}" {
path = "${vsphere_folder.terraform_deployed.path}/{{ folder['group'] }}"
type = "vm"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
{% endif %}
{% endfor %}
{% for folder in terraform_vms %}
{% if folder['inventory_parent_folder'] is defined %}
resource "vsphere_folder" "{{ folder['group'] }}" {
path = "${vsphere_folder.{{ folder['inventory_parent_folder'] }}.path}/{{ folder['group'] }}"
type = "vm"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
{% endif %}
{% endfor %}
What we are doing above is first iterating all of our top level parent folders and ensuring that we do not have any duplicates, and then iterating through each child folder to define our complete inventory resources structure. And after this file is generated it would look like below:
resource "vsphere_folder" "terraform_deployed" {
path = "Terraform Deployed"
type = "vm"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
resource "vsphere_folder" "Docker" {
path = "${vsphere_folder.terraform_deployed.path}/Docker"
type = "vm"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
resource "vsphere_folder" "docker_lbs" {
path = "${vsphere_folder.Docker.path}/docker_lbs"
type = "vm"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
resource "vsphere_folder" "docker_storage" {
path = "${vsphere_folder.Docker.path}/docker_storage"
type = "vm"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
resource "vsphere_folder" "docker_swarm_managers" {
path = "${vsphere_folder.Docker.path}/docker_swarm_managers"
type = "vm"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
resource "vsphere_folder" "docker_swarm_workers" {
path = "${vsphere_folder.Docker.path}/docker_swarm_workers"
type = "vm"
datacenter_id = "${data.vsphere_datacenter.dc.id}"
}
Terraform VM Resources
Now for our VM resources. We already touched on how the variables are
defined for terraform_vms
above in Terraform VMs. So
now let’s see what this would like here now. Because those variables
are already defined we can use another Jinja2 template to generate our
vm_resources.tf
config. The following is an example of what that
Jinja2 template might look like:
terraform_vm_resources.tf.j2
:
{% for vms in terraform_vms %}
resource "vsphere_virtual_machine" "{{ vms['group'] }}" {
count = "${length(var.vms_{{ vms['group'] }})}"
name = "${var.vms_{{ vms['group'] }}[count.index]}"
datacenter = "${data.vsphere_datacenter.dc.name}"
disk {
datastore = "${var.vsphere_default_vm_datastore}"
template = "${var.linux_vm_template}"
type = "thin"
}
{% if vms['addl_disk'] is defined %}
{% for addl_disk in vms['addl_disk'] %}
disk {
datastore = "${var.vsphere_default_vm_datastore}"
size = "{{ addl_disk['size'] }}"
name = "${var.vms_{{ vms['group'] }}[count.index]}_{{ loop.index }}"
type = "thin"
}
{% endfor %}
{% endif %}
dns_servers = "${var.dns_servers}"
domain = "${var.pri_domain_name}"
folder = "${vsphere_folder.{{ vms['group'] }}.path}"
memory = {{ vms['memory_mb'] }}
network_interface {
label = "${var.vsphere_default_vm_network}"
}
vcpu = {{ vms['vcpu'] }}
}
{% endfor %}
And if we were to run this through Ansible to generate our
vm_resources.tf
config it would look similar to below:
resource "vsphere_virtual_machine" "docker_lbs" {
count = "${length(var.vms_docker_lbs)}"
name = "${var.vms_docker_lbs[count.index]}"
datacenter = "${data.vsphere_datacenter.dc.name}"
disk {
datastore = "${var.vsphere_default_vm_datastore}"
template = "${var.linux_vm_template}"
type = "thin"
}
dns_servers = "${var.dns_servers}"
domain = "${var.pri_domain_name}"
folder = "${vsphere_folder.docker_lbs.path}"
memory = 512
network_interface {
label = "${var.vsphere_default_vm_network}"
}
vcpu = 1
}
resource "vsphere_virtual_machine" "docker_storage" {
count = "${length(var.vms_docker_storage)}"
name = "${var.vms_docker_storage[count.index]}"
datacenter = "${data.vsphere_datacenter.dc.name}"
disk {
datastore = "${var.vsphere_default_vm_datastore}"
template = "${var.linux_vm_template}"
type = "thin"
}
disk {
datastore = "${var.vsphere_default_vm_datastore}"
size = "100"
name = "${var.vms_docker_storage[count.index]}_1"
type = "thin"
}
dns_servers = "${var.dns_servers}"
domain = "${var.pri_domain_name}"
folder = "${vsphere_folder.docker_storage.path}"
memory = 512
network_interface {
label = "${var.vsphere_default_vm_network}"
}
vcpu = 1
}
resource "vsphere_virtual_machine" "docker_swarm_managers" {
count = "${length(var.vms_docker_swarm_managers)}"
name = "${var.vms_docker_swarm_managers[count.index]}"
datacenter = "${data.vsphere_datacenter.dc.name}"
disk {
datastore = "${var.vsphere_default_vm_datastore}"
template = "${var.linux_vm_template}"
type = "thin"
}
dns_servers = "${var.dns_servers}"
domain = "${var.pri_domain_name}"
folder = "${vsphere_folder.docker_swarm_managers.path}"
memory = 1024
network_interface {
label = "${var.vsphere_default_vm_network}"
}
vcpu = 1
}
resource "vsphere_virtual_machine" "docker_swarm_workers" {
count = "${length(var.vms_docker_swarm_workers)}"
name = "${var.vms_docker_swarm_workers[count.index]}"
datacenter = "${data.vsphere_datacenter.dc.name}"
disk {
datastore = "${var.vsphere_default_vm_datastore}"
template = "${var.linux_vm_template}"
type = "thin"
}
dns_servers = "${var.dns_servers}"
domain = "${var.pri_domain_name}"
folder = "${vsphere_folder.docker_swarm_workers.path}"
memory = 4096
network_interface {
label = "${var.vsphere_default_vm_network}"
}
vcpu = 1
}
And once again we can see how easy this has been to maintain consistency across our project.
Terraform Ansible Playbook
Below is an example of the Ansible playbook which we will use to put everything together in order to generate all of our Terraform configuration files and then also plan and deploy.
---
- hosts: localhost
connection: local
become: false
gather_facts: false
vars:
terraform_apply: false
terraform_destroy: false
terraform_dir: ../terraform
tasks:
- name: Capturing Terraform Command
command: which terraform
register: _terraform_command
changed_when: false
tags:
- terraform_apply
- terraform_destroy
- terraform_init
- terraform_inventory
- terraform_plan
- name: Setting Terraform Command Path
set_fact:
terraform_command: "{{ _terraform_command['stdout'] }}"
tags:
- terraform_apply
- terraform_destroy
- terraform_init
- terraform_inventory
- terraform_plan
- name: Generating Terraform Variables
template:
src: templates/terraform_variables.tf.j2
dest: "{{ terraform_dir }}/variables.tf"
tags:
- terraform_apply
- terraform_destroy
- terraform_init
- terraform_inventory
- terraform_plan
- name: Generating Secret Terraform Variables
template:
src: templates/terraform.tfvars.j2
dest: "{{ terraform_dir }}/terraform.tfvars"
tags:
- terraform_apply
- terraform_destroy
- terraform_init
- terraform_inventory
- terraform_plan
- name: Generating Terraform Inventory Resources
template:
src: templates/terraform_inventory_resources.tf.j2
dest: "{{ terraform_dir }}/inventory_resources.tf"
tags:
- terraform_apply
- terraform_destroy
- terraform_init
- terraform_inventory
- terraform_plan
- name: Generating Terraform VM Definitions
template:
src: templates/terraform_vm_resources.tf.j2
dest: "{{ terraform_dir }}/vm_resources.tf"
tags:
- terraform_apply
- terraform_destroy
- terraform_init
- terraform_inventory
- terraform_plan
- name: Generating Terraform PDNS Resources
template:
src: templates/terraform_pdns_resources.tf.j2
dest: "{{ terraform_dir }}/pdns_resources.tf"
tags:
- terraform_apply
- terraform_destroy
- terraform_init
- terraform_inventory
- terraform_plan
- name: Capturing Terraform Init State
command: "{{ terraform_command }} init -get=true -input=false"
register: _terraform_init_state
changed_when: false
args:
chdir: "{{ terraform_dir }}"
tags:
- terraform_init
- name: Running Terraform Plan
command: "{{ terraform_command }} plan -out=tfplan -input=false -detailed-exitcode"
register: _terraform_plan
changed_when: false
args:
chdir: "{{ terraform_dir }}"
tags:
- terraform_apply
- terraform_plan
failed_when: _terraform_plan['rc'] > 2
- name: Displaying Terraform Plan
debug: var=_terraform_plan
tags:
- terraform_apply
- terraform_plan
- name: Applying Terraform Plan
command: "{{ terraform_command }} apply -input=false -auto-approve=true tfplan"
register: _terraform_apply
changed_when: false
args:
chdir: "{{ terraform_dir }}"
tags:
- terraform_apply
when: >
_terraform_plan['rc'] == 2 and
terraform_apply
- name: Destroying Terraform Plan
command: "{{ terraform_command }} destroy -force"
register: _terraform_destroy
changed_when: false
args:
chdir: "{{ terraform_dir }}"
tags:
- terraform_destroy
when: terraform_destroy
- name: Capturing Terraform State
command: "{{ terraform_command }} state pull"
register: _terraform_state
changed_when: false
args:
chdir: "{{ terraform_dir }}"
tags:
- terraform_apply
- terraform_destroy
- terraform_inventory
- name: Generating Terraform Ansible Inventory
template:
src: terraform.inv.j2
dest: "{{ vsphere_inventory_directory }}/terraform.inv"
tags:
- terraform_apply
- terraform_destroy
- terraform_inventory
Terraform Ansible Inventory
And of course none of this would be beneficial to any post Terraform deployment in order to provision our VMs with some additional Ansible playbooks without an inventory. In order to dynamically generate this during our Terraform playbook example above we have to capture the Terraform State at the very end of our playbook.
NOTE: Capturing the Terraform State actually just displays what is in the
terraform.tfstate
file in our project folder. So you can also get creative with this a bit more as well.
If you look at the tasks below which are from the playbook example above you will see how we are accomplishing this.
- name: Capturing Terraform State
command: "{{ terraform_command }} state pull"
register: _terraform_state
changed_when: false
args:
chdir: "{{ terraform_dir }}"
tags:
- terraform_apply
- terraform_destroy
- terraform_inventory
- name: Generating Terraform Ansible Inventory
template:
src: terraform.inv.j2
dest: "{{ vsphere_inventory_directory }}/terraform.inv"
tags:
- terraform_apply
- terraform_destroy
- terraform_inventory
And the terraform.inv.j2
Jinja2 template might look something like:
{% set _groups = [] %}
{% for key, value in (_terraform_state['stdout']|from_json)['modules'][0]['resources'].items() %}
{% if 'vsphere_virtual_machine' in key %}
{% set _group = key.split('.') %}
{% set _groups = _groups.append(_group[1]) %}
{% endif %}
{% endfor %}
{% set groups = _groups|unique|list %}
{% for group in groups %}
[{{ group }}]
{% for key, value in (_terraform_state['stdout']|from_json)['modules'][0]['resources'].items() %}
{% if 'vsphere_virtual_machine' in key %}
{% set _group = key.split('.') %}
{% if _group[1] == group %}
{{ value['primary']['attributes']['name'] }}
{% endif %}
{% endif %}
{% endfor %}
{% endfor %}
[terraform_vms]
{% for key, value in (_terraform_state['stdout']|from_json)['modules'][0]['resources'].items() %}
{% if 'vsphere_virtual_machine' in key %}
{{ value['primary']['attributes']['name'] }} ansible_host={{ value['primary']['attributes']['network_interface.0.ipv4_address'] }} mac_address={{ value['primary']['attributes']['network_interface.0.mac_address'] }} uuid={{ value['primary']['attributes']['uuid'] }}
{% endif %}
{% endfor %}
And once we generate our inventory with Ansible it might look a little something like below:
terraform.inv
:
[docker_storage]
docker-storage-02.lab.etsbv.internal
docker-storage-01.lab.etsbv.internal
[docker_swarm_managers]
docker-mgr-01.lab.etsbv.internal
docker-mgr-02.lab.etsbv.internal
docker-mgr-03.lab.etsbv.internal
[docker_swarm_workers]
docker-wrk-03.lab.etsbv.internal
docker-wrk-04.lab.etsbv.internal
docker-wrk-01.lab.etsbv.internal
docker-wrk-02.lab.etsbv.internal
[docker_lbs]
docker-lb-02.lab.etsbv.internal
docker-lb-01.lab.etsbv.internal
[terraform_vms]
docker-storage-02.lab.etsbv.internal ansible_host=10.0.102.178 mac_address=00:50:56:aa:01:a0 uuid=422ad693-162f-1c32-b90c-eee1b0a73d2b
docker-storage-01.lab.etsbv.internal ansible_host=10.0.102.162 mac_address=00:50:56:aa:5f:cb uuid=422a264a-0816-60ff-475c-23af6c0b9d0e
docker-mgr-01.lab.etsbv.internal ansible_host=10.0.102.171 mac_address=00:50:56:aa:a9:c0 uuid=422abe05-4483-88f8-34b7-e354fdc7a211
docker-mgr-02.lab.etsbv.internal ansible_host=10.0.102.166 mac_address=00:50:56:aa:ba:a0 uuid=422a5d74-4de2-1df8-646b-ca62311f98ab
docker-mgr-03.lab.etsbv.internal ansible_host=10.0.102.179 mac_address=00:50:56:aa:e3:06 uuid=422a8d34-68f7-a7be-9c6a-18949ce809ed
docker-wrk-03.lab.etsbv.internal ansible_host=10.0.102.207 mac_address=00:50:56:aa:6b:d5 uuid=422a809a-0cd2-cd84-756b-0822bc3f813a
docker-wrk-04.lab.etsbv.internal ansible_host=10.0.102.183 mac_address=00:50:56:aa:b5:43 uuid=422aae57-67e8-50d8-66f6-3a11bdc87a78
docker-wrk-01.lab.etsbv.internal ansible_host=10.0.102.155 mac_address=00:50:56:aa:e4:93 uuid=422a87fe-baa2-75d6-e666-36c15f351269
docker-wrk-02.lab.etsbv.internal ansible_host=10.0.102.201 mac_address=00:50:56:aa:e0:36 uuid=422a49e3-746c-b550-189c-0e0179c60418
docker-lb-02.lab.etsbv.internal ansible_host=10.0.102.160 mac_address=00:50:56:aa:f8:25 uuid=422adcf8-347a-e9a5-e113-00114c1d2de9
docker-lb-01.lab.etsbv.internal ansible_host=10.0.102.163 mac_address=00:50:56:aa:9c:b3 uuid=422a4adb-e7d3-ea74-a69a-3ff10c13063f
Final Notes
So there you have it. A creative way to use Ansible to generate/execute you Terraform deployments. I have been using this methodology for a short while now and it has been incredibly useful. Is it perfect? Of course not. I would love to hear your thoughts and etc.
Enjoy!
Leave a comment