Table of Contents
terraform.py
is a dynamic Ansible inventory script to connect to systems by
reading Terraform's .tfstate
files. It currently supports:
- AWS (
aws_instance
) - Google Cloud (
google_compute_instance
) - Openstack (
openstack_compute_instance_v2
') - DigitalOcean (
digitalocean_droplet
) - Azure (
azure_instance
) - AzureRM (
azure_virtual_machine
) - VMware vSphere (
vsphere_virtual_machine
) - CenturyLinkCloud (
clc_server
) - SoftLayer (
softlayer_virtualserver
) (Unofficial) - Dimension Data CloudControl (
ddcloud_server
) (Unofficial)
terraform.py
just needs to be on the filesystem of your control machine and
marked as executable. If you're distributing your Ansible configuration via git,
you could just add this repository as a git submodule and pin it to the
release you want. Otherwise, copying terraform.py
to a convenient
place on your filesystem should do the trick.
Make sure that you've annotated your resources with "tags" that correspond to the sshUser for the machine.
Example, for EC2 resources, add a tags entry of "sshUser" equal to "ec2-user":
tags {
Name = "cheese"
sshUser = "ec2-user"
}
Example, for AzureRM resources, add tags entries for ssh_user, role and ssh_ip based on the Network Interface you plan to access the instance from:
tags {
ssh_user = "azurerm-user"
ssh_ip = "${azurerm_network_interface.my_nic.private_ip_address}"
role = "myrole"
}
Next, specify terraform.py
as an inventory source for any Ansible command. For
instance, to use this to test if your servers are working:
ansible -i terraform.py -m ping all
Or in a playbook:
ansible-playbook -i terraform.py your_playbook.yml
terraform.py
is with matching versions of
mantl. Run terraform.py --version
to
report the current version.
To add a new provider, you need to implement a parser for that provider's
resources in terraform.py
. Parsers should only be implemented for resources
that Ansible can connect to and modify (instances, nodes, whatever your platform
calls them.) A parser is a function that takes a resource (a dictionary
processed from JSON) and outputs a tuple of (name, attributes, groups)
. The
parser should be decorated with the parses
decorator, which takes the name of
the resource (EG aws_instance
). It should also be decorated with
calculate_mantl_vars
.
As a guideline, terraform.py
should require no resources outside the Python
standard distribution so it can just be copied in wherever it's needed.
Terraform's state files represent node attributes as a flat dictionary, but contain nested information within them. This tends to look something like this:
...
"disk.#": 1,
"disk.0.auto_delete": "true",
"disk.0.device_name": ""
...
It's much easier to work with nested information in Ansible, so terraform.py
has three sub-parsers to transform these structures:
parse_attr_list
takes a dictionary, a prefix, and an optional separator and
returns a list of dictionaries. The limited "disk" example above would become:
[{"auto_delete": "true", "device_name": ""}]
parse_dict
takes a dictionary, a prefix, and an optional separator and returns
a dictionary. Given keys like this and the prefix "metadata":
...
"metadata.#": "3",
"metadata.dc": "gce-dc"
...
parse_dict
would return something like this:
{"dc": "gce-dc"}
Lists are rarer in Terraform states, but still occur in things like tag lists.
parse_list
takes a dictionary, a prefix, and an optional separator and returns
a list. Given keys like this and the prefix "keys":
...
"tags.#": "2",
"tags.2783239913": "mantl",
"tags.3990563915": "control",
...
parse_list
would return this:
["mantl", "control"]
Copyright © 2015 Cisco Systems, Inc.
Licensed under the Apache License, Version 2.0 (the "License").
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.