File ansible-terraform-inventory-0.10.obscpio of Package ansible-terraform-inventory

07070100000000000081A400000000000000000000000161046BE50000003E000000000000000000000000000000000000002C00000000ansible-terraform-inventory-0.10/.gitignore/fixtures/secrets.tfvars
/pkg/*.zip
/terraform-inventory
.idea07070100000001000081A400000000000000000000000161046BE500000065000000000000000000000000000000000000002D00000000ansible-terraform-inventory-0.10/.travis.ymllanguage: go

env:
- GO111MODULE=on

go:
  - "1.13"
  - "1.x" # latest

script:
  - go test -v ./...
07070100000002000081A400000000000000000000000161046BE50000043F000000000000000000000000000000000000002900000000ansible-terraform-inventory-0.10/LICENSEMIT License

Copyright (c) 2016 Adam Mckaig (and contributors)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
07070100000003000081A400000000000000000000000161046BE5000018DD000000000000000000000000000000000000002B00000000ansible-terraform-inventory-0.10/README.md# Terraform Inventory

[![Build Status](https://travis-ci.org/adammck/terraform-inventory.svg?branch=master)](https://travis-ci.org/adammck/terraform-inventory)
[![GitHub release](https://img.shields.io/github/release/adammck/terraform-inventory.svg?maxAge=2592000)](https://github.com/adammck/terraform-inventory/releases)
[![GitHub release](https://img.shields.io/homebrew/v/terraform-inventory.svg?maxAge=2592000)](http://braumeister.org/formula/terraform-inventory)

This is a little Go app which generates a dynamic [Ansible][ans] inventory from
a [Terraform][tf] state file. It allows one to spawn a bunch of instances with 
Terraform, then (re-)provision them with Ansible.

The following providers are supported:

* AWS
* CloudStack
* DigitalOcean
* Docker
* Exoscale
* Google Compute Engine
* Hetzner Cloud
* [libvirt](https://github.com/dmacvicar/terraform-provider-libvirt)
* Linode
* OpenStack
* Packet
* ProfitBricks
* Scaleway
* SoftLayer
* VMware
* Nutanix
* Open Telekom Cloud
* Yandex.Cloud
* Telmate/Proxmox

It's very simple to add support for new providers. See pull requests with the
[provider][pv] label for examples.


# Help Wanted 🙋

This library is stable, but I've been neglecting it somewhat on account of no
longer using Ansible at work. Please drop me a line if you'd be interested in
helping to maintain this tool.


# Installation

On OSX, install it with Homebrew:

	brew install terraform-inventory

Alternatively, you can download a [release][rel] suitable for your platform and
unzip it. Make sure the `terraform-inventory` binary is executable, and you're
ready to go.


## Usage

If you are using [remote state][rs] (or if your state file happens to be named
`terraform.tfstate`), `cd` to it and run:

	ansible-playbook --inventory-file=/path/to/terraform-inventory deploy/playbook.yml

This will provide the resource names and IP addresses of any instances found in
the state file to Ansible, which can then be used as hosts patterns in your
playbooks. For example, given for the following Terraform config:

	resource "digitalocean_droplet" "my_web_server" {
	  image = "centos-7-0-x64"
	  name = "web-1"
	  region = "nyc1"
	  size = "512mb"
	}

The corresponding playbook might look like:

	- hosts: my_web_server
	  tasks:
	    - yum: name=cowsay
	    - command: cowsay hello, world!

Note that the instance was identified by its _resource name_ from the Terraform
config, not its _instance name_ from the provider. On AWS, resources are also
grouped by their tags. For example:

	resource "aws_instance" "my_web_server" {
	  instance_type = "t2.micro"
	  ami = "ami-96a818fe"
	  tags = {
	    Role = "web"
	    Env = "dev"
	  }
	}

	resource "aws_instance" "my_worker" {
	  instance_type = "t2.micro"
	  ami = "ami-96a818fe"
	  tags = {
	    Role = "worker"
	    Env = "dev"
	  }
	}

Can be provisioned separately with:

	- hosts: role_web
	  tasks:
	    - command: cowsay this is a web server!

	- hosts: role_worker
	  tasks:
	    - command: cowsay this is a worker server!

	- hosts: env_dev
	  tasks:
	    - command: cowsay this runs on all dev servers!


## More Usage

Ansible doesn't seem to support calling a dynamic inventory script with params,
so if you need to specify the location of your state file or terraform directory, set the `TF_STATE`
environment variable before running `ansible-playbook`, like:


	TF_STATE=deploy/terraform.tfstate ansible-playbook --inventory-file=/path/to/terraform-inventory deploy/playbook.yml

	or

	TF_STATE=../terraform ansible-playbook --inventory-file=/path/to/terraform-inventory deploy/playbook.yml

If `TF_STATE` is a file, it parses the file as json, if `TF_STATE` is a directory, it runs `terraform state pull` inside the directory, which is supports both local and remote terraform state.

It looks for state config in this order

- `TF_STATE`: environment variable of where to find either a statefile or a terraform project
- `TI_TFSTATE`: another environment variable similar to TF_STATE
- `terraform.tfstate`: it looks in the state file in the current directory.
- `.`: lastly it assumes you are at the root of a terraform project.

Alternately, if you need to do something fancier (like downloading your state
file from S3 before running), you might wrap this tool with a shell script, and
call that instead. Something like:

	#!/bin/bash
	/path/to/terraform-inventory $@ deploy/terraform.tfstate

Then run Ansible with the script as an inventory:

	ansible-playbook --inventory-file=bin/inventory deploy/playbook.yml

This tool returns the public IP of the host by default. If you require the private
IP of the instance to run Ansible, set the `TF_KEY_NAME` environment variable
to `private_ip` before running the playbook, like:

	TF_KEY_NAME=private_ip ansible-playbook --inventory-file=/path/to/terraform-inventory deploy/playbook.yml

By default, the ip address is the ansible inventory name. The `TF_HOSTNAME_KEY_NAME` environment variable allows
you to overwrite the source of the ansible inventory name.

	TF_HOSTNAME_KEY_NAME=name ansible-playbook --inventory-file=/path/to/terraform-inventory deploy/playbook.yml

## Development

It's just a Go app, so the usual:

	go get github.com/adammck/terraform-inventory

To test against an example statefile, run:

	terraform-inventory --list fixtures/example.tfstate
	terraform-inventory --host=52.7.58.202 fixtures/example.tfstate

To update the fixtures, populate `fixtures/secrets.tfvars` with your DO and AWS
account details, and run `fixtures/update`. To run a tiny Ansible playbook on
the example resourecs, run:

	TF_STATE=fixtures/example.tfstate ansible-playbook --inventory-file=/path/to/terraform-inventory fixtures/playbook.yml

You almost certainly don't need to do any of this. Use the tests instead.


## Acknowledgements

Development of
[#14](https://github.com/adammck/terraform-inventory/issues/14),
[#16](https://github.com/adammck/terraform-inventory/issues/16),
and [#22](https://github.com/adammck/terraform-inventory/issues/22)
was generously sponsored by [Transloadit](https://transloadit.com).


## License

MIT.

[ans]: https://www.ansible.com
[tf]:  https://www.terraform.io
[rel]: https://github.com/adammck/terraform-inventory/releases
[rs]:  https://www.terraform.io/docs/state/remote.html
[pv]:  https://github.com/adammck/terraform-inventory/pulls?q=is%3Apr+label%3Aprovider
07070100000004000041ED00000000000000000000000261046BE500000000000000000000000000000000000000000000002500000000ansible-terraform-inventory-0.10/bin07070100000005000081ED00000000000000000000000161046BE50000022F000000000000000000000000000000000000002A00000000ansible-terraform-inventory-0.10/bin/dist#!/bin/bash -e
cd $(cd `dirname "$0"`; cd ..; pwd)

VERSION=$1
if [ -z $VERSION ]; then
	echo "Usage: $0 VERSION"
	exit 1
fi

TARGETS="darwin_amd64 linux_386 linux_amd64 linux_arm windows_386 windows_amd64"

for target in $TARGETS; do
	t=(${target//_/ })

	export GOOS=${t[0]}
	export GOARCH=${t[1]}
	export NAME=terraform-inventory

	if [ $GOOS == "windows" ]; then
		NAME=${NAME}.exe
	fi

	go build -ldflags "-X main.build_version=$VERSION" -o pkg/${NAME}

	pushd pkg
	zip terraform-inventory_${VERSION}_${GOOS}_${GOARCH}.zip ${NAME}
	rm ${NAME}
	popd
done
07070100000006000081A400000000000000000000000161046BE50000252E000000000000000000000000000000000000002800000000ansible-terraform-inventory-0.10/cli.gopackage main

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"sort"
	"strings"
)

type counterSorter struct {
	resources []*Resource
}

func (cs counterSorter) Len() int {
	return len(cs.resources)
}

func (cs counterSorter) Swap(i, j int) {
	cs.resources[i], cs.resources[j] = cs.resources[j], cs.resources[i]
}

func (cs counterSorter) Less(i, j int) bool {
	return cs.resources[i].counterNumeric < cs.resources[j].counterNumeric || (cs.resources[i].counterNumeric == cs.resources[j].counterNumeric && cs.resources[i].counterStr < cs.resources[j].counterStr)
}

type allGroup struct {
	Hosts []string               `json:"hosts"`
	Vars  map[string]interface{} `json:"vars"`
}

func appendUniq(strs []string, item string) []string {
	if len(strs) == 0 {
		strs = append(strs, item)
		return strs
	}
	sort.Strings(strs)
	i := sort.SearchStrings(strs, item)
	if i == len(strs) || (i < len(strs) && strs[i] != item) {
		strs = append(strs, item)
	}
	return strs
}

func gatherResources(s *stateAnyTerraformVersion) map[string]interface{} {
	if s.TerraformVersion == TerraformVersionPre0dot12 {
		return gatherResourcesPre0dot12(&s.StatePre0dot12)
	} else if s.TerraformVersion == TerraformVersion0dot12 {
		return gatherResources0dot12(&s.State0dot12)
	} else {
		panic("Unimplemented Terraform version enum")
	}
}

func gatherResourcesPre0dot12(s *state) map[string]interface{} {
	outputGroups := make(map[string]interface{})

	all := &allGroup{Hosts: make([]string, 0), Vars: make(map[string]interface{})}
	types := make(map[string][]string)
	individual := make(map[string][]string)
	ordered := make(map[string][]string)
	tags := make(map[string][]string)

	unsortedOrdered := make(map[string][]*Resource)

	resourceIDNames := s.mapResourceIDNames()
	for _, res := range s.resources() {
		// place in list of all resources
		all.Hosts = appendUniq(all.Hosts, res.Hostname())

		// place in list of resource types
		tp := fmt.Sprintf("type_%s", res.resourceType)
		types[tp] = appendUniq(types[tp], res.Hostname())

		unsortedOrdered[res.baseName] = append(unsortedOrdered[res.baseName], res)

		// store as individual host (e.g. <name>_<count>)
		var invdName string
		if res.counterStr != "" {
			invdName = fmt.Sprintf("%s_%s", res.baseName, strings.Replace(res.counterStr, ".", "_", -1))
		} else {
			invdName = fmt.Sprintf("%s_%d", res.baseName, res.counterNumeric)
		}
		if old, exists := individual[invdName]; exists {
			fmt.Fprintf(os.Stderr, "overwriting already existing individual key %s, old: %v, new: %v\n", invdName, old, res.Hostname())
		}
		individual[invdName] = []string{res.Hostname()}

		// inventorize tags
		for k, v := range res.Tags() {
			// Valueless
			tag := k
			if v != "" {
				tag = fmt.Sprintf("%s_%s", k, v)
			}
			// if v is a resource ID, then tag should be resource name
			if _, exists := resourceIDNames[v]; exists {
				tag = resourceIDNames[v]
			}
			tags[tag] = appendUniq(tags[tag], res.Hostname())
		}
	}

	// inventorize outputs as variables
	if len(s.outputs()) > 0 {
		for _, out := range s.outputs() {
			all.Vars[out.keyName] = out.value
		}
	}

	// sort the ordered groups
	for basename, resources := range unsortedOrdered {
		cs := counterSorter{resources}
		sort.Sort(cs)

		for i := range resources {
			ordered[basename] = append(ordered[basename], resources[i].Hostname())
		}
	}

	outputGroups["all"] = all
	for k, v := range individual {
		if old, exists := outputGroups[k]; exists {
			fmt.Fprintf(os.Stderr, "individual overwriting already existing output with key %s, old: %v, new: %v", k, old, v)
		}
		outputGroups[k] = v
	}
	for k, v := range ordered {
		if old, exists := outputGroups[k]; exists {
			fmt.Fprintf(os.Stderr, "ordered overwriting already existing output with key %s, old: %v, new: %v", k, old, v)
		}
		outputGroups[k] = v
	}
	for k, v := range types {
		if old, exists := outputGroups[k]; exists {
			fmt.Fprintf(os.Stderr, "types overwriting already existing output key %s, old: %v, new: %v", k, old, v)
		}
		outputGroups[k] = v
	}
	for k, v := range tags {
		if old, exists := outputGroups[k]; exists {
			fmt.Fprintf(os.Stderr, "tags overwriting already existing output key %s, old: %v, new: %v", k, old, v)
		}
		outputGroups[k] = v
	}

	return outputGroups
}

func gatherResources0dot12(s *stateTerraform0dot12) map[string]interface{} {
	outputGroups := make(map[string]interface{})

	all := &allGroup{Hosts: make([]string, 0), Vars: make(map[string]interface{})}
	types := make(map[string][]string)
	individual := make(map[string][]string)
	ordered := make(map[string][]string)
	tags := make(map[string][]string)

	unsortedOrdered := make(map[string][]*Resource)

	resourceIDNames := s.mapResourceIDNames()
	for _, res := range s.resources() {
		// place in list of all resources
		all.Hosts = appendUniq(all.Hosts, res.Hostname())

		// place in list of resource types
		tp := fmt.Sprintf("type_%s", res.resourceType)
		types[tp] = appendUniq(types[tp], res.Hostname())
		sort.Strings(types[tp])

		unsortedOrdered[res.baseName] = append(unsortedOrdered[res.baseName], res)

		// store as individual host (e.g. <name>_<count>)
		var invdName string
		if res.counterStr != "" {
			invdName = fmt.Sprintf("%s_%s", res.baseName, strings.Replace(res.counterStr, ".", "_", -1))
		} else {
			invdName = fmt.Sprintf("%s_%d", res.baseName, res.counterNumeric)
		}
		if old, exists := individual[invdName]; exists {
			fmt.Fprintf(os.Stderr, "overwriting already existing individual key %s, old: %v, new: %v", invdName, old, res.Hostname())
		}
		individual[invdName] = []string{res.Hostname()}

		// inventorize tags
		for k, v := range res.Tags() {
			// Valueless
			tag := k
			if v != "" {
				tag = fmt.Sprintf("%s_%s", k, v)
			}
			// if v is a resource ID, then tag should be resource name
			if _, exists := resourceIDNames[v]; exists {
				tag = resourceIDNames[v]
			}
			tags[tag] = appendUniq(tags[tag], res.Hostname())
			sort.Strings(tags[tag])
		}
	}

	sort.Strings(all.Hosts)

	// inventorize outputs as variables
	if len(s.outputs()) > 0 {
		for _, out := range s.outputs() {
			all.Vars[out.keyName] = out.value
		}
	}

	// sort the ordered groups
	for basename, resources := range unsortedOrdered {
		cs := counterSorter{resources}
		sort.Sort(cs)

		for i := range resources {
			ordered[basename] = append(ordered[basename], resources[i].Hostname())
		}
	}

	outputGroups["all"] = all
	for k, v := range individual {
		if old, exists := outputGroups[k]; exists {
			fmt.Fprintf(os.Stderr, "individual overwriting already existing output with key %s, old: %v, new: %v", k, old, v)
		}
		outputGroups[k] = v
	}
	for k, v := range ordered {
		if old, exists := outputGroups[k]; exists {
			fmt.Fprintf(os.Stderr, "ordered overwriting already existing output with key %s, old: %v, new: %v", k, old, v)
		}
		outputGroups[k] = v
	}
	for k, v := range types {
		if old, exists := outputGroups[k]; exists {
			fmt.Fprintf(os.Stderr, "types overwriting already existing output key %s, old: %v, new: %v", k, old, v)
		}
		outputGroups[k] = v
	}
	for k, v := range tags {
		if old, exists := outputGroups[k]; exists {
			fmt.Fprintf(os.Stderr, "tags overwriting already existing output key %s, old: %v, new: %v", k, old, v)
		}
		outputGroups[k] = v
	}

	return outputGroups
}

func cmdList(stdout io.Writer, stderr io.Writer, s *stateAnyTerraformVersion) int {
	return output(stdout, stderr, gatherResources(s))
}

func cmdInventory(stdout io.Writer, stderr io.Writer, s *stateAnyTerraformVersion) int {
	groups := gatherResources(s)
	group_names := []string{}
	for group, _ := range groups {
		group_names = append(group_names, group)
	}
	sort.Strings(group_names)
	for _, group := range group_names {

		switch grp := groups[group].(type) {
		case []string:
			writeLn("["+group+"]", stdout, stderr)
			for _, item := range grp {
				writeLn(item, stdout, stderr)
			}

		case *allGroup:
			writeLn("["+group+"]", stdout, stderr)
			for _, item := range grp.Hosts {
				writeLn(item, stdout, stderr)
			}
			writeLn("", stdout, stderr)
			writeLn("["+group+":vars]", stdout, stderr)
			vars := []string{}
			for key, _ := range grp.Vars {
				vars = append(vars, key)
			}
			sort.Strings(vars)
			for _, key := range vars {
				jsonItem, _ := json.Marshal(grp.Vars[key])
				itemLn := fmt.Sprintf("%s", string(jsonItem))
				writeLn(key+"="+itemLn, stdout, stderr)
			}
		}

		writeLn("", stdout, stderr)
	}

	return 0
}

func writeLn(str string, stdout io.Writer, stderr io.Writer) {
	_, err := io.WriteString(stdout, str+"\n")
	checkErr(err, stderr)
}

func checkErr(err error, stderr io.Writer) int {
	if err != nil {
		fmt.Fprintf(stderr, "Error writing inventory: %s\n", err)
		return 1
	}
	return 0
}

func cmdHost(stdout io.Writer, stderr io.Writer, s *stateAnyTerraformVersion, hostname string) int {
	for _, res := range s.resources() {
		if hostname == res.Hostname() {
			attributes := res.Attributes()
			attributes["ansible_host"] = res.Address()
			return output(stdout, stderr, attributes)
		}
	}

	fmt.Fprintf(stdout, "{}")
	return 1
}

// output marshals an arbitrary JSON object and writes it to stdout, or writes
// an error to stderr, then returns the appropriate exit code.
func output(stdout io.Writer, stderr io.Writer, whatever interface{}) int {
	b, err := json.Marshal(whatever)
	if err != nil {
		fmt.Fprintf(stderr, "Error encoding JSON: %s\n", err)
		return 1
	}

	_, err = stdout.Write(b)
	if err != nil {
		fmt.Fprintf(stderr, "Error writing JSON: %s\n", err)
		return 1
	}

	return 0
}
07070100000007000081A400000000000000000000000161046BE50000000D000000000000000000000000000000000000002D00000000ansible-terraform-inventory-0.10/cli_test.gopackage main
07070100000008000041ED00000000000000000000000261046BE500000000000000000000000000000000000000000000002A00000000ansible-terraform-inventory-0.10/fixtures07070100000009000081A400000000000000000000000161046BE50000041F000000000000000000000000000000000000003500000000ansible-terraform-inventory-0.10/fixtures/example.tfvariable "do_token" {}
variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "aws_subnet_id" {}

provider "aws" {
    access_key = "${var.aws_access_key}"
    secret_key = "${var.aws_secret_key}"
    region = "us-east-1"
}

provider "digitalocean" {
  token = "${var.do_token}"
}

resource "aws_instance" "alpha" {
  ami = "ami-96a818fe"
  instance_type = "t2.micro"
  subnet_id = "${var.aws_subnet_id}"
  associate_public_ip_address = true
  key_name = "terraform-inventory"
  count = 2

  root_block_device = {
    delete_on_termination = true
  }

  tags = {
    Role = "Web"
  }
}

resource "aws_instance" "beta" {
  ami = "ami-96a818fe"
  instance_type = "t2.micro"
  subnet_id = "${var.aws_subnet_id}"
  associate_public_ip_address = true
  key_name = "terraform-inventory"

  root_block_device = {
    delete_on_termination = true
  }

  tags = {
    Role = "Worker"
  }
}

resource "digitalocean_droplet" "gamma" {
  image = "centos-7-0-x64"
  name = "terraform-inventory-1"
  region = "nyc1"
  size = "512mb"
  ssh_keys = [862272]
}
0707010000000A000081A400000000000000000000000161046BE5000027A9000000000000000000000000000000000000003A00000000ansible-terraform-inventory-0.10/fixtures/example.tfstate{
    "version": 1,
    "serial": 17,
    "modules": [
        {
            "path": [
                "root"
            ],
            "outputs": {
                "datacenter": "mydc"
            },
            "resources": {
                "vsphere_virtual_machine.test": {
                    "type": "vsphere_virtual_machine",
                    "primary": {
                        "id": "test1",
                        "attributes": {
                            "cluster": "Cluster",
                            "datacenter": "Datacenter",
                            "detach_unknown_disks_on_delete": "false",
                            "disk.#": "1",
                            "disk.4123483162.bootable": "false",
                            "disk.4123483162.controller_type": "scsi",
                            "disk.4123483162.datastore": "Datastore",
                            "disk.4123483162.iops": "0",
                            "disk.4123483162.keep_on_remove": "false",
                            "disk.4123483162.key": "0",
                            "disk.4123483162.name": "",
                            "disk.4123483162.size": "0",
                            "disk.4123483162.template": "template",
                            "disk.4123483162.type": "thin",
                            "disk.4123483162.uuid": "",
                            "disk.4123483162.vmdk": "",
                            "dns_servers.#": "2",
                            "dns_servers.0": "8.8.8.8",
                            "dns_servers.1": "8.8.4.4",
                            "domain": "vsphere.local",
                            "enable_disk_uuid": "false",
                            "folder": "",
                            "id": "test1",
                            "linked_clone": "false",
                            "memory": "1024",
                            "memory_reservation": "0",
                            "name": "test1",
                            "network_interface.#": "1",
                            "network_interface.0.adapter_type": "",
                            "network_interface.0.ip_address": "",
                            "network_interface.0.ipv4_address": "1.2.3.4",
                            "network_interface.0.ipv4_gateway": "1.2.3.4",
                            "network_interface.0.ipv4_prefix_length": "22",
                            "network_interface.0.ipv6_address": "",
                            "network_interface.0.ipv6_gateway": "",
                            "network_interface.0.ipv6_prefix_length": "64",
                            "network_interface.0.label": "VM Network",
                            "network_interface.0.mac_address": "",
                            "network_interface.0.subnet_mask": "",
                            "skip_customization": "false",
                            "time_zone": "Etc/UTC",
                            "uuid": "42323d21-8455-92c7-838c-ea0efefea2222",
                            "vcpu": "1"
                        },
                        "meta": {
                            "schema_version": "1"
                        },
                        "tainted": false
                    }
                },
                "aws_instance.alpha.0": {
                    "type": "aws_instance",
                    "primary": {
                        "id": "i-92f79124",
                        "attributes": {
                            "ami": "ami-96a818fe",
                            "associate_public_ip_address": "true",
                            "availability_zone": "us-east-1d",
                            "ebs_block_device.#": "0",
                            "ebs_optimized": "false",
                            "ephemeral_block_device.#": "0",
                            "iam_instance_profile": "",
                            "id": "i-92f79124",
                            "instance_type": "t2.micro",
                            "key_name": "terraform-inventory",
                            "monitoring": "false",
                            "private_dns": "ip-10-0-0-4.ec2.internal",
                            "private_ip": "10.0.0.4",
                            "public_dns": "",
                            "public_ip": "52.90.134.71",
                            "root_block_device.#": "1",
                            "root_block_device.0.delete_on_termination": "true",
                            "root_block_device.0.iops": "0",
                            "root_block_device.0.volume_size": "8",
                            "root_block_device.0.volume_type": "standard",
                            "security_groups.#": "0",
                            "source_dest_check": "true",
                            "subnet_id": "subnet-59f9b32e",
                            "tags.#": "1",
                            "tags.Role": "Web",
                            "tenancy": "default",
                            "vpc_security_group_ids.#": "1",
                            "vpc_security_group_ids.2076429742": "sg-b42329d0"
                        },
                        "meta": {
                            "schema_version": "1"
                        }
                    }
                },
                "aws_instance.alpha.1": {
                    "type": "aws_instance",
                    "primary": {
                        "id": "i-23f69095",
                        "attributes": {
                            "ami": "ami-96a818fe",
                            "associate_public_ip_address": "true",
                            "availability_zone": "us-east-1d",
                            "ebs_block_device.#": "0",
                            "ebs_optimized": "false",
                            "ephemeral_block_device.#": "0",
                            "iam_instance_profile": "",
                            "id": "i-23f69095",
                            "instance_type": "t2.micro",
                            "key_name": "terraform-inventory",
                            "monitoring": "false",
                            "private_dns": "ip-10-0-0-9.ec2.internal",
                            "private_ip": "10.0.0.9",
                            "public_dns": "",
                            "public_ip": "52.90.154.150",
                            "root_block_device.#": "1",
                            "root_block_device.0.delete_on_termination": "true",
                            "root_block_device.0.iops": "0",
                            "root_block_device.0.volume_size": "8",
                            "root_block_device.0.volume_type": "standard",
                            "security_groups.#": "0",
                            "source_dest_check": "true",
                            "subnet_id": "subnet-59f9b32e",
                            "tags.#": "1",
                            "tags.Role": "Web",
                            "tenancy": "default",
                            "vpc_security_group_ids.#": "1",
                            "vpc_security_group_ids.2076429742": "sg-b42329d0"
                        },
                        "meta": {
                            "schema_version": "1"
                        }
                    }
                },
                "aws_instance.beta": {
                    "type": "aws_instance",
                    "primary": {
                        "id": "i-c2f79174",
                        "attributes": {
                            "ami": "ami-96a818fe",
                            "associate_public_ip_address": "true",
                            "availability_zone": "us-east-1d",
                            "ebs_block_device.#": "0",
                            "ebs_optimized": "false",
                            "ephemeral_block_device.#": "0",
                            "iam_instance_profile": "",
                            "id": "i-c2f79174",
                            "instance_type": "t2.micro",
                            "key_name": "terraform-inventory",
                            "monitoring": "false",
                            "private_dns": "ip-10-0-0-8.ec2.internal",
                            "private_ip": "10.0.0.8",
                            "public_dns": "",
                            "public_ip": "52.23.224.202",
                            "root_block_device.#": "1",
                            "root_block_device.0.delete_on_termination": "true",
                            "root_block_device.0.iops": "0",
                            "root_block_device.0.volume_size": "8",
                            "root_block_device.0.volume_type": "standard",
                            "security_groups.#": "0",
                            "source_dest_check": "true",
                            "subnet_id": "subnet-59f9b32e",
                            "tags.#": "1",
                            "tags.Role": "Worker",
                            "tenancy": "default",
                            "vpc_security_group_ids.#": "1",
                            "vpc_security_group_ids.2076429742": "sg-b42329d0"
                        },
                        "meta": {
                            "schema_version": "1"
                        }
                    }
                },
                "digitalocean_droplet.gamma": {
                    "type": "digitalocean_droplet",
                    "primary": {
                        "id": "9411223",
                        "attributes": {
                            "id": "9411223",
                            "image": "centos-7-0-x64",
                            "ipv4_address": "198.211.99.194",
                            "locked": "false",
                            "name": "terraform-inventory-1",
                            "region": "nyc1",
                            "size": "512mb",
                            "ssh_keys.#": "1",
                            "ssh_keys.0": "862272",
                            "status": "active"
                        }
                    }
                }
            }
        }
    ]
}
0707010000000B000081A400000000000000000000000161046BE50000004D000000000000000000000000000000000000003700000000ansible-terraform-inventory-0.10/fixtures/playbook.yml- hosts:
    - alpha
    - beta
  tasks:
    - command: "echo Hello, world!"
0707010000000C000081A400000000000000000000000161046BE500000049000000000000000000000000000000000000004100000000ansible-terraform-inventory-0.10/fixtures/secrets.tfvars.exampledo_token = ""
aws_access_key = ""
aws_secret_key = ""
aws_subnet_id = ""
0707010000000D000081ED00000000000000000000000161046BE5000000A7000000000000000000000000000000000000003100000000ansible-terraform-inventory-0.10/fixtures/update#!/bin/bash -e
cd $(cd `dirname "$0"`; cd ..; pwd)
terraform apply -refresh -var-file="fixtures/secrets.tfvars" -state="fixtures/example.tfstate" -backup="-" fixtures
0707010000000E000081A400000000000000000000000161046BE5000000C1000000000000000000000000000000000000002800000000ansible-terraform-inventory-0.10/go.modmodule github.com/adammck/terraform-inventory

go 1.16

require (
	github.com/adammck/venv v0.0.0-20160819025605-8a9c907a37d3
	github.com/blang/vfs v1.0.0
	github.com/stretchr/testify v1.4.0
)
0707010000000F000081A400000000000000000000000161046BE500000544000000000000000000000000000000000000002800000000ansible-terraform-inventory-0.10/go.sumgithub.com/adammck/venv v0.0.0-20160819025605-8a9c907a37d3 h1:O+yARMhWahUt25JGj1yEdUdQX6JlKkSZ3eZARw2rp80=
github.com/adammck/venv v0.0.0-20160819025605-8a9c907a37d3/go.mod h1:3zXR2a/VSQndtpShh783rUTaEA2mpqN2VqZclBARBc0=
github.com/blang/vfs v1.0.0 h1:AUZUgulCDzbaNjTRWEP45X7m/J10brAptZpSRKRZBZc=
github.com/blang/vfs v1.0.0/go.mod h1:jjuNUc/IKcRNNWC9NUCvz4fR9PZLPIKxEygtPs/4tSI=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
07070100000010000081A400000000000000000000000161046BE500000174000000000000000000000000000000000000002A00000000ansible-terraform-inventory-0.10/input.gopackage main

import (
	"github.com/adammck/venv"
	"github.com/blang/vfs"
)

func GetInputPath(fs vfs.Filesystem, env venv.Env) string {

	var fn string

	fn = env.Getenv("TF_STATE")
	if fn != "" {
		return fn
	}

	fn = env.Getenv("TI_TFSTATE")
	if fn != "" {
		return fn
	}

	fn = "terraform.tfstate"
	_, err := fs.Stat(fn)
	if err == nil {
		return fn
	}

	return "."
}
07070100000011000081A400000000000000000000000161046BE5000007FE000000000000000000000000000000000000002F00000000ansible-terraform-inventory-0.10/input_test.gopackage main

import (
	"io"
	"os"
	"path/filepath"
	"testing"

	"github.com/adammck/venv"
	"github.com/blang/vfs"
	"github.com/blang/vfs/memfs"
	"github.com/stretchr/testify/assert"
)

func TestGetInputPath(t *testing.T) {
	assert.Equal(t, ".", GetInputPath(memfs.Create(), venv.Mock()))
	assert.Equal(t, "aaa", GetInputPath(memfs.Create(), envWith(map[string]string{"TF_STATE": "aaa"})))
	assert.Equal(t, "bbb", GetInputPath(memfs.Create(), envWith(map[string]string{"TI_TFSTATE": "bbb"})))
	assert.Equal(t, "terraform.tfstate", GetInputPath(fsWithFiles([]string{"terraform.tfstate"}), venv.Mock()))
	assert.Equal(t, ".", GetInputPath(fsWithFiles([]string{".terraform/terraform.tfstate"}), venv.Mock()))
	assert.Equal(t, "terraform", GetInputPath(fsWithDirs([]string{"terraform"}), envWith(map[string]string{"TF_STATE": "terraform"})))
}

func envWith(env map[string]string) venv.Env {
	e := venv.Mock()

	for k, v := range env {
		e.Setenv(k, v)
	}

	return e
}

func fsWithFiles(filenames []string) vfs.Filesystem {
	fs := memfs.Create()
	var err error

	for _, fn := range filenames {

		path := filepath.Dir(fn)
		if path != "" {
			err = vfs.MkdirAll(fs, path, 0700)
			if err != nil {
				panic(err)
			}
		}

		err = touchFile(fs, fn)
		if err != nil {
			panic(err)
		}
	}

	return fs
}

func fsWithDirs(dirs []string) vfs.Filesystem {
	fs := memfs.Create()

	var err error

	for _, fp := range dirs {
		err = vfs.MkdirAll(fs, fp, 0700)
		if err != nil {
			panic(err)
		}
	}

	return fs
}

// TODO: Upgrade this later with file contents.
func touchFile(fs vfs.Filesystem, filename string) error {
	return writeFile(fs, filename, []byte{}, 0600)
}

// port of ioutil.Writefile for vfs
func writeFile(fs vfs.Filesystem, filename string, data []byte, perm os.FileMode) error {
	f, err := fs.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
	if err != nil {
		return err
	}
	n, err := f.Write(data)
	if err == nil && n < len(data) {
		err = io.ErrShortWrite
	}
	if err1 := f.Close(); err == nil {
		err = err1
	}
	return err
}
07070100000012000081A400000000000000000000000161046BE500000B59000000000000000000000000000000000000002900000000ansible-terraform-inventory-0.10/main.gopackage main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"

	"github.com/adammck/venv"
	"github.com/blang/vfs"
)

var version = flag.Bool("version", false, "print version information and exit")
var list = flag.Bool("list", false, "list mode")
var host = flag.String("host", "", "host mode")
var inventory = flag.Bool("inventory", false, "inventory mode")

func main() {
	flag.Parse()
	file := flag.Arg(0)

	if *version == true {
		fmt.Printf("%s version %s\n", os.Args[0], versionInfo())
		return
	}

	fs := vfs.OS()
	if file == "" {

		env := venv.OS()
		file = GetInputPath(fs, env)
	}

	if !*list && *host == "" && !*inventory {
		fmt.Fprint(os.Stderr, "Either --host or --list must be specified")
		os.Exit(1)
	}

	path, err := filepath.Abs(file)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Invalid file: %s\n", err)
		os.Exit(1)
	}

	f, err := fs.Stat(path)

	if err != nil {
		fmt.Fprintf(os.Stderr, "Invalid file: %s\n", err)
		os.Exit(1)
	}

	var s stateAnyTerraformVersion

	if !f.IsDir() {
		stateFile, err := os.Open(path)
		defer stateFile.Close()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error opening tfstate file: %s\n", err)
			os.Exit(1)
		}

		err = s.read(stateFile)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error reading tfstate file: %s\n", err)
			os.Exit(1)
		}
	}

	if f.IsDir() {
		cmd := exec.Command("terraform", "show", "-json")
		cmd.Dir = path
		var out bytes.Buffer
		cmd.Stdout = &out

		err = cmd.Run()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error running `terraform show -json` in directory %s, %s, falling back to trying Terraform pre-0.12 command\n", path, err)

			cmd = exec.Command("terraform", "state", "pull")
			cmd.Dir = path
			out.Reset()
			cmd.Stdout = &out
			err = cmd.Run()

			if err != nil {
				fmt.Fprintf(os.Stderr, "Error running `terraform state pull` in directory %s, %s\n", path, err)
				os.Exit(1)
			}
		}

		err = s.read(&out)

		if err != nil {
			fmt.Fprintf(os.Stderr, "Error reading Terraform state: %s\n", err)
			os.Exit(1)
		}
	}

	if s.TerraformVersion == TerraformVersionUnknown {
		fmt.Fprintf(os.Stderr, "Unknown state format\n\nUsage: %s [options] path\npath: this is either a path to a state file or a folder from which `terraform commands` are valid\n", os.Args[0])
		os.Exit(1)
	}

	if (s.TerraformVersion == TerraformVersionPre0dot12 && s.StatePre0dot12.Modules == nil) ||
		(s.TerraformVersion == TerraformVersion0dot12 && s.State0dot12.Values.RootModule == nil) {
		fmt.Fprintf(os.Stderr, "No modules found in state\n\nUsage: %s [options] path\npath: this is either a path to a state file or a folder from which `terraform commands` are valid\n", os.Args[0])
		os.Exit(1)
	}

	if *list {
		os.Exit(cmdList(os.Stdout, os.Stderr, &s))
	} else if *inventory {
		os.Exit(cmdInventory(os.Stdout, os.Stderr, &s))
	} else if *host != "" {
		os.Exit(cmdHost(os.Stdout, os.Stderr, &s, *host))
	}
}
07070100000013000081A400000000000000000000000161046BE5000001A2000000000000000000000000000000000000002B00000000ansible-terraform-inventory-0.10/output.gopackage main

import (
	"fmt"
)

type Output struct {

	// The keyName and value of the output
	keyName string
	value   interface{}
}

func NewOutput(keyName string, value interface{}) (*Output, error) {

	// TODO: Warn instead of silently ignore error?
	if len(keyName) == 0 {
		return nil, fmt.Errorf("couldn't parse output keyName: %s", keyName)
	}

	return &Output{
		keyName: keyName,
		value:   value,
	}, nil
}
07070100000014000081A400000000000000000000000161046BE500002BA3000000000000000000000000000000000000002B00000000ansible-terraform-inventory-0.10/parser.gopackage main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"sort"
	"strconv"
	"strings"
)

// TerraformVersion defines which version of Terraform state applies
type TerraformVersion int

const (
	// TerraformVersionUnknown means unknown version
	TerraformVersionUnknown TerraformVersion = 0

	// TerraformVersionPre0dot12 means < 0.12
	TerraformVersionPre0dot12 TerraformVersion = 1

	// TerraformVersion0dot12 means >= 0.12
	TerraformVersion0dot12 TerraformVersion = 2
)

type stateAnyTerraformVersion struct {
	StatePre0dot12   state
	State0dot12      stateTerraform0dot12
	TerraformVersion TerraformVersion
}

// Terraform < v0.12
type state struct {
	Modules []moduleState `json:"modules"`
}
type moduleState struct {
	Path           []string                 `json:"path"`
	ResourceStates map[string]resourceState `json:"resources"`
	Outputs        map[string]interface{}   `json:"outputs"`
}
type resourceState struct {
	// Populated from statefile
	Type    string        `json:"type"`
	Primary instanceState `json:"primary"`
}
type instanceState struct {
	ID         string            `json:"id"`
	Attributes map[string]string `json:"attributes,omitempty"`
}

// Terraform <= v0.12
type stateTerraform0dot12 struct {
	Values valuesStateTerraform0dot12 `json:"values"`
}
type valuesStateTerraform0dot12 struct {
	RootModule *moduleStateTerraform0dot12 `json:"root_module"`
	Outputs    map[string]interface{}      `json:"outputs"`
}
type moduleStateTerraform0dot12 struct {
	ResourceStates []resourceStateTerraform0dot12 `json:"resources"`
	ChildModules   []moduleStateTerraform0dot12   `json:"child_modules"`
	Address        string                         `json:"address"` // empty for root module, else e.g. `module.mymodulename`
}
type resourceStateTerraform0dot12 struct {
	Address   string                 `json:"address"`
	Index     *interface{}           `json:"index"` // only set by Terraform for counted resources
	Name      string                 `json:"name"`
	RawValues map[string]interface{} `json:"values"`
	Type      string                 `json:"type"`
}

// read populates the state object from a statefile.
func (s *stateAnyTerraformVersion) read(stateFile io.Reader) error {
	s.TerraformVersion = TerraformVersionUnknown

	b, readErr := ioutil.ReadAll(stateFile)
	if readErr != nil {
		return readErr
	}

	err0dot12 := json.Unmarshal(b, &(*s).State0dot12)
	if err0dot12 == nil && s.State0dot12.Values.RootModule != nil {
		s.TerraformVersion = TerraformVersion0dot12
	} else {
		errPre0dot12 := json.Unmarshal(b, &(*s).StatePre0dot12)
		if errPre0dot12 == nil && s.StatePre0dot12.Modules != nil {
			s.TerraformVersion = TerraformVersionPre0dot12
		} else {
			return fmt.Errorf("0.12 format error: %v; pre-0.12 format error: %v (nil error means no content/modules found in the respective format)", err0dot12, errPre0dot12)
		}
	}

	return nil
}

// outputs returns a slice of the Outputs found in the statefile.
func (s *state) outputs() []*Output {
	inst := make([]*Output, 0)

	for _, m := range s.Modules {
		for k, v := range m.Outputs {
			var o *Output
			switch v := v.(type) {
			case map[string]interface{}:
				o, _ = NewOutput(k, v["value"])
			case string:
				o, _ = NewOutput(k, v)
			default:
				o, _ = NewOutput(k, "<error>")
			}

			inst = append(inst, o)
		}
	}

	return inst
}

// outputs returns a slice of the Outputs found in the statefile.
func (s *stateTerraform0dot12) outputs() []*Output {
	inst := make([]*Output, 0)

	for k, v := range s.Values.Outputs {
		var o *Output
		switch v := v.(type) {
		case map[string]interface{}:
			o, _ = NewOutput(k, v["value"])
		default: // not expected
			o, _ = NewOutput(k, "<error>")
		}

		inst = append(inst, o)
	}

	return inst
}

// map of resource ID -> resource Name
func (s *state) mapResourceIDNames() map[string]string {
	t := map[string]string{}

	for _, m := range s.Modules {
		for _, k := range m.resourceKeys() {
			if m.ResourceStates[k].Primary.ID != "" && m.ResourceStates[k].Primary.Attributes["name"] != "" {
				kk := strings.ToLower(m.ResourceStates[k].Primary.ID)
				t[kk] = m.ResourceStates[k].Primary.Attributes["name"]
			}
		}
	}
	return t
}

// map of resource ID -> resource Name
func (s *stateTerraform0dot12) mapResourceIDNames() map[string]string {
	t := map[string]string{}

	for _, module := range s.getAllModules() {
		for _, resourceState := range module.ResourceStates {
			id, typeOk := resourceState.RawValues["id"].(string)
			if typeOk && id != "" && resourceState.Name != "" {
				k := strings.ToLower(id)

				if val, ok := resourceState.RawValues["category_id"]; ok && resourceState.Type == "vsphere_tag" {
					if categoryID, typeOk := val.(string); typeOk {
						if categoryName := s.getResourceIDName(categoryID); categoryName != "" {
							t[k] = fmt.Sprintf("%s_%s", s.getResourceIDName(categoryID), resourceState.Name)
							continue
						}
					}
				}

				t[k] = resourceState.Name
			}
		}
	}

	return t
}

func (s *stateTerraform0dot12) getResourceIDName(matchingID string) string {
	for _, module := range s.getAllModules() {
		for _, resourceState := range module.ResourceStates {
			id, typeOk := resourceState.RawValues["id"].(string)
			if typeOk && id == matchingID {
				return resourceState.Name
			}
		}
	}

	return ""
}

func (s *stateTerraform0dot12) getAllModules() []*moduleStateTerraform0dot12 {
	var allModules []*moduleStateTerraform0dot12
	allModules = append(allModules, s.Values.RootModule)
	addChildModules(&allModules, s.Values.RootModule)
	return allModules
}

// recursively adds all child modules to the slice
func addChildModules(out *[]*moduleStateTerraform0dot12, from *moduleStateTerraform0dot12) {
	for i := range from.ChildModules {
		addChildModules(out, &from.ChildModules[i])
		*out = append(*out, &from.ChildModules[i])
	}
}

// resources returns a slice of the Resources found in the statefile.
func (s *stateAnyTerraformVersion) resources() []*Resource {
	switch s.TerraformVersion {
	case TerraformVersionPre0dot12:
		return s.StatePre0dot12.resources()
	case TerraformVersion0dot12:
		return s.State0dot12.resources()
	case TerraformVersionUnknown:
	}
	panic("Unimplemented Terraform version enum")
}

// resources returns a slice of the Resources found in the statefile.
func (s *state) resources() []*Resource {
	inst := make([]*Resource, 0)

	for _, m := range s.Modules {
		for _, k := range m.resourceKeys() {
			if strings.HasPrefix(k, "data.") {
				// This does not represent a host (e.g. AWS AMI)
				continue
			}

			// If a module is used, the resource key may not be unique, for instance:
			//
			// The module cannot use dynamic resource naming and thus has to use some hardcoded name:
			//
			//     resource "aws_instance" "host" { ... }
			//
			// The main file then uses the module twice:
			//
			//     module "application1" { source = "./modules/mymodulename" }
			//     module "application2" { source = "./modules/mymodulename" }
			//
			// Avoid key clashes by prepending module name to the key. If path is ["root"], don't
			// prepend anything.
			//
			// In the above example: `aws_instance.host` -> `aws_instance.application1_host`
			fullKey := k
			resourceNameIndex := strings.Index(fullKey, ".") + 1
			if len(m.Path) > 1 && resourceNameIndex > 0 {
				for i := len(m.Path) - 1; i >= 1; i-- {
					fullKey = fullKey[:resourceNameIndex] + strings.Replace(m.Path[i], ".", "_", -1) + "_" + fullKey[resourceNameIndex:]
				}
			}

			// Terraform stores resources in a name->map map, but we need the name to
			// decide which groups to include the resource in. So wrap it in a higher-
			// level object with both properties.
			r, err := NewResource(fullKey, m.ResourceStates[k])
			if err != nil {
				asJSON, _ := json.Marshal(m.ResourceStates[k])
				fmt.Fprintf(os.Stderr, "Warning: failed to parse resource %s (%v)\n", asJSON, err)
				continue
			}
			if r.IsSupported() {
				inst = append(inst, r)
			}
		}
	}

	return inst
}

func encodeTerraform0Dot12ValuesAsAttributes(rawValues *map[string]interface{}) map[string]string {
	ret := make(map[string]string)
	for k, v := range *rawValues {
		switch v := v.(type) {
		case map[string]interface{}:
			ret[k+".#"] = strconv.Itoa(len(v))
			for kk, vv := range v {
				if str, typeOk := vv.(string); typeOk {
					ret[k+"."+kk] = str
				} else {
					ret[k+"."+kk] = "<error>"
				}
			}
		case []interface{}:
			ret[k+".#"] = strconv.Itoa(len(v))
			for kk, vv := range v {
				switch o := vv.(type) {
				case string:
					ret[k+"."+strconv.Itoa(kk)] = o
				case map[string]interface{}:
					for kkk, vvv := range o {
						if str, typeOk := vvv.(string); typeOk {
							ret[k+"."+strconv.Itoa(kk)+"."+kkk] = str
						} else {
							ret[k+"."+strconv.Itoa(kk)+"."+kkk] = "<error>"
						}
					}
				default:
					ret[k+"."+strconv.Itoa(kk)] = "<error>"
				}
			}
		case string:
			ret[k] = v
		default:
			ret[k] = "<error>"
		}
	}
	return ret
}

// resources returns a slice of the Resources found in the statefile.
func (s *stateTerraform0dot12) resources() []*Resource {
	inst := make([]*Resource, 0)

	for _, module := range s.getAllModules() {
		for _, rs := range module.ResourceStates {
			id, typeOk := rs.RawValues["id"].(string)
			if !typeOk {
				continue
			}

			if strings.HasPrefix(rs.Address, "data.") {
				// This does not represent a host (e.g. AWS AMI)
				continue
			}

			modulePrefix := ""
			if module.Address != "" {
				modulePrefix = strings.Replace(module.Address, ".", "_", -1) + "_"
			}
			resourceKeyName := rs.Type + "." + modulePrefix + rs.Name
			if rs.Index != nil {
				i := *rs.Index
				switch v := i.(type) {
				case int:
					resourceKeyName += "." + strconv.Itoa(v)
				case float64:
					resourceKeyName += "." + strconv.Itoa(int(v))
				case string:
					resourceKeyName += "." + strings.Replace(v, ".", "_", -1)
				default:
					fmt.Fprintf(os.Stderr, "Warning: unknown index type %v\n", v)
				}
			}

			// Terraform stores resources in a name->map map, but we need the name to
			// decide which groups to include the resource in. So wrap it in a higher-
			// level object with both properties.
			//
			// Convert to the pre-0.12 structure for backwards compatibility of code.
			r, err := NewResource(resourceKeyName, resourceState{
				Type: rs.Type,
				Primary: instanceState{
					ID:         id,
					Attributes: encodeTerraform0Dot12ValuesAsAttributes(&rs.RawValues),
				},
			})
			if err != nil {
				asJSON, _ := json.Marshal(rs)
				fmt.Fprintf(os.Stderr, "Warning: failed to parse resource %s (%v)\n", asJSON, err)
				continue
			}
			if r.IsSupported() {
				inst = append(inst, r)
			}
		}
	}

	sort.Slice(inst, func(i, j int) bool {
		return inst[i].baseName < inst[j].baseName
	})

	return inst
}

// resourceKeys returns a sorted slice of the key names of the resources in this
// module. Do this instead of range over ResourceStates, to ensure that the
// output is consistent.
func (ms *moduleState) resourceKeys() []string {
	lk := len(ms.ResourceStates)
	keys := make([]string, lk, lk)
	i := 0

	for k := range ms.ResourceStates {
		keys[i] = k
		i++
	}

	sort.Strings(keys)
	return keys
}
07070100000015000081A400000000000000000000000161046BE5000080A3000000000000000000000000000000000000003000000000ansible-terraform-inventory-0.10/parser_test.gopackage main

import (
	"bytes"
	"encoding/json"
	"os"
	"strings"
	"testing"

	"github.com/stretchr/testify/assert"
)

const exampleStateFileEnvHostname = `
{
	"version": 1,
	"serial": 1,
	"modules": [
		{
			"resources": {
				"libvirt_domain.fourteen": {
					"type": "libvirt_domain",
					"primary": {
						"id": "824c29be-2164-44c8-83e0-787705571d95",
						"attributes": {
							"name": "fourteen",
							"network_interface.#": "1",
							"network_interface.0.addresses.#": "1",
							"network_interface.0.addresses.0": "192.168.102.14",
							"network_interface.0.mac": "96:EE:4D:BD:B2:45"
						}
					}
				}
			}
		}
	]
}`

const expectedListOutputEnvHostname = `
{
	"all":	 {
		"hosts": [
			"fourteen"
		],
		"vars": {
		}
	},
	"fourteen":	 ["fourteen"],
	"fourteen_0":	 ["fourteen"],
	"type_libvirt_domain": ["fourteen"]
}`

const exampleStateFile = `
{
	"version": 1,
	"serial": 1,
	"modules": [
		{
			"path": [
				"root"
			],
			"outputs": {
					"olddatacenter": "<0.7_format",
					"datacenter": {
						"sensitive": false,
						"type": "string",
						"value": "mydc"
					},
					"ids": {
						"type": "list",
						"value": [1, 2, 3, 4]
					},
					"map": {
						"type": "map",
						"value": {
							"key": "value"
						}
					}
			},
			"resources": {
				"aws_instance.one.0": {
					"type": "aws_instance",
					"primary": {
						"id": "i-aaaaaaaa",
						"attributes": {
							"id": "i-aaaaaaaa",
							"private_ip": "10.0.0.1",
							"tags.%": "1",
							"tags.Role": "Web"
						}
					}
				},
				"aws_instance.dup.0": {
					"type": "aws_instance",
					"primary": {
						"id": "i-aaaaaaaa",
						"attributes": {
							"id": "i-aaaaaaaa",
							"private_ip": "10.0.0.1",
							"tags.#": "1",
							"tags.Role": "Web"
						}
					}
				},
				"aws_instance.one.1": {
					"type": "aws_instance",
					"primary": {
						"id": "i-a1a1a1a1",
						"attributes": {
							"id": "i-a1a1a1a1",
							"private_ip": "10.0.1.1"
						}
					}
				},
				"aws_instance.two": {
					"type": "aws_instance",
					"primary": {
						"id": "i-bbbbbbbb",
						"attributes": {
							"id": "i-bbbbbbbb",
							"private_ip": "10.0.0.2",
							"public_ip": "50.0.0.1"
						}
					}
				},
				"aws_security_group.example": {
					"type": "aws_security_group",
					"primary": {
						"id": "sg-cccccccc",
						"attributes": {
							"id": "sg-cccccccc",
							"description": "Whatever"
						}
					}
				},
				"digitalocean_droplet.three": {
					"type": "digitalocean_droplet",
					"primary": {
						"id": "ddddddd",
						"attributes": {
							"id": "ddddddd",
							"ipv4_address": "192.168.0.3",
							"tags.#": "2",
							"tags.1": "staging",
							"tags.2": "webserver"
						}
					}
				},
				"cloudstack_instance.four": {
					"type": "cloudstack_instance",
					"primary": {
						"id": "aaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
						"attributes": {
							"id": "aaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
							"ipaddress": "10.2.1.5",
							"name": "terraform-inventory-4",
							"service_offering": "small",
							"template": "centos-7-0-x64",
							"zone": "nyc2"
						}
					}
				},
				"vsphere_virtual_machine.five": {
					"type": "vsphere_virtual_machine",
					"primary": {
						"id": "aaaaaaaa",
						"attributes": {
							"custom_configuration_parameters.%": "1",
							"custom_configuration_parameters.role": "rrrrrrrr",
							"datacenter": "dddddddd",
							"host": "hhhhhhhh",
							"id": "aaaaaaaa",
							"image": "Ubunutu 14.04 LTS",
							"network_interface.0.ipv4_address": "10.20.30.40",
							"linked_clone": "false",
							"name": "nnnnnn",
							"power_on": "true"
						}
					}
				},
				"openstack_compute_instance_v2.six": {
					"type": "openstack_compute_instance_v2",
					"primary": {
						"id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
						"attributes": {
							"id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
							"access_ip_v4": "10.120.0.226",
							"access_ip_v6": "",
							"tag.%": "1",
							"tag.ostags": "rock",
							"metadata.status": "superServer",
							"metadata.#": "very bad",
							"metadata_toes": "faada2142412jhb1j2"
						}
					}
				},
				"softlayer_virtual_guest.seven": {
					"type": "softlayer_virtual_guest",
					"primary": {
						"id": "12345678",
						"attributes": {
							"id":"12345678",
							"ipv4_address_private":"10.0.0.7",
							"ipv4_address":""
						}
					}
				},
				"google_compute_instance.eight": {
					"type": "123456789",
					"primary": {
						"id": "123456789",
						"attributes": {
							"network_interface.0.access_config.0.assigned_nat_ip": "10.0.0.8",
							"network_interface.0.access_config.0.nat_ip": "10.0.0.8",
							"network_interface.0.address": "10.0.0.8",
							"tags.#": "1",
							"tags.1201918879": "database",
							"tags_fingerprint": "AqbISNuzJIs=",
							"zone": "europe-west1-d"
						}
					}
				},
				"exoscale_compute.nine": {
					"type": "exoscale_compute",
					"depends_on": [
						"x",
						"y"
					],
					"primary": {
						"id": "5730e2c9-765f-46e1-aa77-81c94f56ce5d",
						"attributes": {
							"diskSize": "10",
							"id": "5730e2c9-765f-46e1-aa77-81c94f56ce5d",
							"keypair": "kp",
							"name": "xyz",
							"gateway": "10.0.0.1",
							"ip4": "true",
							"ip6": "false",
							"ip6_address": "",
							"ip6_cidr": "",
							"ip_address": "10.0.0.9",
							"networks.#": "1",
							"networks.0.%": "5",
							"networks.0.default": "true",
							"networks.0.ip4address": "10.0.0.9",
							"networks.0.ip6address": "",
							"networks.0.networkname": "guestNetworkForBasicZone",
							"networks.0.type": "Shared",
							"securitygroups.#": "1",
							"securitygroups.0": "xyz",
							"size": "zzz",
							"state": "Running",
							"tags.%": "1",
							"tags.Role": "nine",
							"template": "Linux CoreOS stable 1298 64-bit",
							"userdata": "",
							"zone": "ch-gva-2"
						},
						"meta": {},
						"tainted": false
					},
					"deposed": [],
					"provider": ""
				},
				"triton_machine.ten": {
					"type": "triton_machine",
					"depends_on": [],
					"primary": {
						"id": "123456789",
						"attributes": {
							"administrator_pw": "",
							"cloud_config": "",
							"dataset": "dset1",
							"disk": "25600",
							"domain_names.#": "",
							"domain_names.0": "",
							"domain_names.1": "",
							"domain_names.2": "",
							"domain_names.3": "",
							"firewall_enabled": "true",
							"id": "123456789",
							"image": "",
							"ips.#": "1",
							"ips.0": "10.0.0.10",
							"memory": "1024",
							"metadata.%": "0",
							"name": "triton_ten",
							"networks.#": "2",
							"networks.0": "1",
							"networks.1": "2",
							"nic.#": "1",
							"nic.1.gateway": "",
							"nic.1.ip": "10.0.0.10",
							"nic.1.mac": "",
							"nic.1.netmask": "",
							"nic.1.network": "",
							"nic.1.primary": "true",
							"nic.1.state": "running",
							"package": "g4-highcpu-1G",
							"primaryip": "10.0.0.10",
							"tags.%": "1",
							"tags.Role": "test",
							"type": "smartmachine",
							"user_data": ""
						},
						"meta": {},
						"tainted": false
					},
					"deposed": [],
					"provider": ""
				},
				"scaleway_server.eleven": {
					"type": "scaleway_server",
					"depends_on": [],
					"primary": {
						"id": "490c369b-e062-4951-b1c5-f9a8ccee8a38",
						"attributes": {
							"enable_ipv6": "true",
							"id": "490c369b-e062-4951-b1c5-f9a8ccee8a38",
							"image": "ab8fbe9e-b13c-46a5-9139-ae7ae73569f0",
							"name": "eleven",
							"private_ip": "11.0.0.11",
							"public_ip": "10.0.0.11",
							"public_ipv6": "2001:bc8:4400:2500::e:800",
							"security_group": "92a62362-72ab-4864-a94e-f02557153218",
							"state": "running",
							"state_detail": "booted",
							"tags.#": "1",
							"tags.0": "scw_test",
							"type": "VC1S"
						},
						"meta": {},
						"tainted": false
					},
					"deposed": [],
					"provider": "provider.scaleway"
				},
				"vsphere_virtual_machine.twelve": {
					"type": "vsphere_virtual_machine",
					"primary": {
						"id": "422cfa4a-c6bb-3405-0335-2d9b2034405f",
						"attributes": {
							"default_ip_address": "10.20.30.50",
							"tags.#": "1",
							"tags.1357913579": "urn:vmomi:InventoryServiceTag:00000000-0001-4957-81fa-1234567890ab:GLOBAL"
						}
					}
				},
				"data.vsphere_tag.testTag1": {
					"type": "vsphere_tag",
					"primary": {
						"id": "urn:vmomi:InventoryServiceTag:00000000-0001-4957-81fa-1234567890ab:GLOBAL",
						"attributes": {
							"name": "testTag1"
						}
					}
				},
				"packet_device.thirteen": {
					"type": "packet_device",
					"depends_on": [],
					"primary": {
						"id": "e35816e2-b9b4-4ef3-9317-a32b98f6cb44",
						"attributes": {
							"billing_cycle": "hourly",
							"created": "2018-04-02T14:52:34Z",
							"facility": "ewr1",
							"hostname": "sa-test-1",
							"id": "e35816e2-b9b4-4ef3-9317-a32b98f6cb44",
							"locked": "false",
							"network.#": "3",
							"network.0.address": "10.0.0.13",
							"network.0.cidr": "31",
							"network.0.family": "4",
							"network.0.gateway": "10.0.0.254",
							"network.0.public": "true",
							"operating_system": "ubuntu_16_04",
							"plan": "baremetal_0",
							"project_id": "123456d5-087a-4976-877a-45b86584b786",
							"state": "active",
							"tags.#": "0",
							"updated": "2018-04-02T14:57:13Z"
						},
						"meta": {},
						"tainted": false
					},
					"deposed": [],
					"provider": ""
				},
				"libvirt_domain.fourteen": {
					"type": "libvirt_domain",
					"primary": {
						"id": "824c29be-2164-44c8-83e0-787705571d95",
						"attributes": {
							"network_interface.#": "1",
							"network_interface.0.addresses.#": "1",
							"network_interface.0.addresses.0": "192.168.102.14",
							"network_interface.0.mac": "96:EE:4D:BD:B2:45"
						}
					}
				},
				"opentelekomcloud_compute_instance_v2.nineteen": {
					"type": "opentelekomcloud_compute_instance_v2",
					"primary": {
						"id": "00000000-0000-0000-0000-000000000015",
						"attributes": {
							"id": "00000000-0000-0000-0000-000000000015",
							"access_ip_v4": "10.0.0.19",
							"access_ip_v6": "",
							"tag.%": "1",
							"tag.tfinventory": "rocks",
							"metadata.superman": "clarkkent"
						}
					}
				},
				"profitbricks_server.sixteen": {
					"type": "profitbricks_server",
					"primary": {
						"id": "12345678",
						"attributes": {
							"primary_ip": "10.0.0.16"
						}
					}
				},
				"aws_spot_instance_request.seventeen": {
					"type": "aws_spot_instance_request",
					"primary": {
						"id": "i-a1a1a1a1",
						"attributes": {
							"id": "sir-a1a1a1a1",
							"public_ip": "50.0.0.17",
							"tags.%": "1",
							"tags.Role": "worker"
						}
					}
				},
				"linode_instance.eighteen": {
					"type": "linode_instance",
					"depends_on": [],
					"primary": {
						"id": "123456789",
						"attributes": {
							"ip_address": "80.80.100.124",
							"private_ip": "true",
							"private_ip_address": "192.168.167.23",
							"tags.#": "0"
						}
					}
				}
				"telmate_proxmox.nineteen": {
					"type": "telmate_proxmox",
					"primary": {
						"id": "proxmox/qemu/100",
						"attributes": {
							"default_ipv4_address": "192.168.1.123",
							"ssh_host": "192.168.1.123"
						}
					}
				}
			}
		}
	]
}
`

const expectedListOutput = `
{
	"all":	 {
		"hosts": [
			"10.0.0.1",
			"10.0.0.10",
			"10.0.0.11",
			"10.0.0.13",
			"10.0.0.16",
			"10.0.0.19",
			"10.0.0.7",
			"10.0.0.8",
			"10.0.0.9",
			"10.0.1.1",
			"10.120.0.226",
			"10.2.1.5",
			"10.20.30.40",
			"192.168.0.3",
			"192.168.102.14",
			"50.0.0.1",
			"50.0.0.17",
			"80.80.100.124",
			"10.20.30.50"
		],
		"vars": {
			"datacenter": "mydc",
			"olddatacenter": "<0.7_format",
			"ids": [1, 2, 3, 4],
			"map": {"key": "value"}
		}
	},
	"one":	 ["10.0.0.1", "10.0.1.1"],
	"dup":	 ["10.0.0.1"],
	"two":	 ["50.0.0.1"],
	"three": ["192.168.0.3"],
	"four":  ["10.2.1.5"],
	"five":  ["10.20.30.40"],
	"six":	 ["10.120.0.226"],
	"seven": ["10.0.0.7"],
	"eight": ["10.0.0.8"],
	"nine": ["10.0.0.9"],
	"ten": ["10.0.0.10"],
	"eleven": ["10.0.0.11"],
	"twelve": ["10.20.30.50"],
	"testTag1": ["10.20.30.50"],
	"thirteen": ["10.0.0.13"],
	"fourteen": ["192.168.102.14"],
	"nineteen": ["10.0.0.19"],
	"sixteen": ["10.0.0.16"],
	"seventeen": ["50.0.0.17"],
	"eighteen": ["80.80.100.124"],
	"nineteen": ["192.168.1.123"],

	"one_0":   ["10.0.0.1"],
	"dup_0":   ["10.0.0.1"],
	"one_1":   ["10.0.1.1"],
	"two_0":   ["50.0.0.1"],
	"three_0": ["192.168.0.3"],
	"four_0":  ["10.2.1.5"],
	"five_0":  ["10.20.30.40"],
	"six_0":   ["10.120.0.226"],
	"seven_0": ["10.0.0.7"],
	"eight_0": ["10.0.0.8"],
	"nine_0":  ["10.0.0.9"],
	"ten_0":   ["10.0.0.10"],
	"eleven_0": ["10.0.0.11"],
	"twelve_0": ["10.20.30.50"],
	"thirteen_0": ["10.0.0.13"],
	"fourteen_0": ["192.168.102.14"],
	"nineteen_0": ["10.0.0.19"],
	"sixteen_0": ["10.0.0.16"],
	"seventeen_0": ["50.0.0.17"],
	"eighteen_0": ["80.80.100.124"],
	"nineteen_0": ["192.168.1.123"],

	"type_aws_instance":                  ["10.0.0.1", "10.0.1.1", "50.0.0.1"],
	"type_digitalocean_droplet":          ["192.168.0.3"],
	"type_cloudstack_instance":           ["10.2.1.5"],
	"type_vsphere_virtual_machine":       ["10.20.30.40", "10.20.30.50"],
	"type_openstack_compute_instance_v2": ["10.120.0.226"],
	"type_opentelekomcloud_compute_instance_v2": ["10.0.0.19"],
	"type_profitbricks_server":           ["10.0.0.16"],
	"type_softlayer_virtual_guest":       ["10.0.0.7"],
	"type_exoscale_compute":              ["10.0.0.9"],
	"type_google_compute_instance":       ["10.0.0.8"],
	"type_triton_machine":                ["10.0.0.10"],
	"type_scaleway_server":               ["10.0.0.11"],
	"type_packet_device":                 ["10.0.0.13"],
	"type_libvirt_domain":                ["192.168.102.14"],
	"type_aws_spot_instance_request":			["50.0.0.17"],
	"type_linode_instance":               ["80.80.100.124"],
	"type_telmate_proxmox":               ["192.168.1.123"],

	"role_nine": ["10.0.0.9"],
	"role_rrrrrrrr": ["10.20.30.40"],
	"role_web": ["10.0.0.1"],
	"role_test": ["10.0.0.10"],
	"role_worker": ["50.0.0.17"],
	"webserver": ["192.168.0.3"],
	"staging": ["192.168.0.3"],
	"status_superserver": ["10.120.0.226"],
	"ostags_rock": ["10.120.0.226"],
	"database": ["10.0.0.8"],
	"scw_test": ["10.0.0.11"],
	"tfinventory_rocks": ["10.0.0.19"],
	"superman_clarkkent": ["10.0.0.19"]
}
`

const expectedInventoryOutput = `[all]
10.0.0.1
10.0.0.10
10.0.0.11
10.0.0.13
10.0.0.16
10.0.0.19
10.0.0.7
10.0.0.8
10.0.0.9
10.0.1.1
10.120.0.226
10.2.1.5
10.20.30.40
192.168.0.3
192.168.102.14
50.0.0.1
50.0.0.17
80.80.100.124
192.168.1.123
10.20.30.50

[all:vars]
datacenter="mydc"
ids=[1,2,3,4]
map={"key":"value"}
olddatacenter="\u003c0.7_format"

[database]
10.0.0.8

[dup]
10.0.0.1

[dup_0]
10.0.0.1

[eight]
10.0.0.8

[eight_0]
10.0.0.8

[eighteen]
80.80.100.124

[eighteen_0]
80.80.100.124

[nineteen]
80.80.100.124

[nineteen_0]
80.80.100.124

[eleven]
10.0.0.11

[eleven_0]
10.0.0.11

[five]
10.20.30.40

[five_0]
10.20.30.40

[four]
10.2.1.5

[four_0]
10.2.1.5

[fourteen]
192.168.102.14

[fourteen_0]
192.168.102.14

[nine]
10.0.0.9

[nine_0]
10.0.0.9

[nineteen]
10.0.0.19

[nineteen_0]
10.0.0.19

[one]
10.0.0.1
10.0.1.1

[one_0]
10.0.0.1

[one_1]
10.0.1.1

[ostags_rock]
10.120.0.226

[role_nine]
10.0.0.9

[role_rrrrrrrr]
10.20.30.40

[role_test]
10.0.0.10

[role_web]
10.0.0.1

[role_worker]
50.0.0.17

[scw_test]
10.0.0.11

[seven]
10.0.0.7

[seven_0]
10.0.0.7

[seventeen]
50.0.0.17

[seventeen_0]
50.0.0.17

[six]
10.120.0.226

[six_0]
10.120.0.226

[sixteen]
10.0.0.16

[sixteen_0]
10.0.0.16

[staging]
192.168.0.3

[status_superserver]
10.120.0.226

[superman_clarkkent]
10.0.0.19

[ten]
10.0.0.10

[ten_0]
10.0.0.10

[testTag1]
10.20.30.50

[tfinventory_rocks]
10.0.0.19

[thirteen]
10.0.0.13

[thirteen_0]
10.0.0.13

[three]
192.168.0.3

[three_0]
192.168.0.3

[twelve]
10.20.30.50

[twelve_0]
10.20.30.50

[two]
50.0.0.1

[two_0]
50.0.0.1

[type_aws_instance]
10.0.0.1
10.0.1.1
50.0.0.1

[type_aws_spot_instance_request]
50.0.0.17

[type_cloudstack_instance]
10.2.1.5

[type_digitalocean_droplet]
192.168.0.3

[type_exoscale_compute]
10.0.0.9

[type_google_compute_instance]
10.0.0.8

[type_libvirt_domain]
192.168.102.14

[type_linode_instance]
80.80.100.124

[type_telmate_proxmox]
191.168.1.123

[type_openstack_compute_instance_v2]
10.120.0.226

[type_opentelekomcloud_compute_instance_v2]
10.0.0.19

[type_packet_device]
10.0.0.13

[type_profitbricks_server]
10.0.0.16

[type_scaleway_server]
10.0.0.11

[type_softlayer_virtual_guest]
10.0.0.7

[type_triton_machine]
10.0.0.10

[type_vsphere_virtual_machine]
10.20.30.40
10.20.30.50

[webserver]
192.168.0.3

`

const expectedHostOneOutput = `
{
	"ansible_host": "10.0.0.1",
	"id":"i-aaaaaaaa",
	"private_ip":"10.0.0.1",
	"tags.#": "1",
	"tags.Role": "Web"
}
`

func TestListCommand(t *testing.T) {
	var s stateAnyTerraformVersion
	r := strings.NewReader(exampleStateFile)
	err := s.read(r)
	assert.NoError(t, err)

	assert.Equal(t, TerraformVersionPre0dot12, s.TerraformVersion)

	// Decode expectation as JSON
	var exp interface{}
	err = json.Unmarshal([]byte(expectedListOutput), &exp)
	assert.NoError(t, err)

	// Run the command, capture the output
	var stdout, stderr bytes.Buffer
	exitCode := cmdList(&stdout, &stderr, &s)
	assert.Equal(t, 0, exitCode)
	assert.Equal(t, "", stderr.String())

	// Decode the output to compare
	var act interface{}
	err = json.Unmarshal([]byte(stdout.String()), &act)
	assert.NoError(t, err)

	assert.Equal(t, exp, act)
}

func TestListCommandEnvHostname(t *testing.T) {
	var s stateAnyTerraformVersion
	r := strings.NewReader(exampleStateFileEnvHostname)
	err := s.read(r)
	assert.NoError(t, err)

	assert.Equal(t, TerraformVersionPre0dot12, s.TerraformVersion)

	// Decode expectation as JSON
	var exp interface{}
	err = json.Unmarshal([]byte(expectedListOutputEnvHostname), &exp)
	assert.NoError(t, err)

	// Run the command, capture the output
	var stdout, stderr bytes.Buffer
	os.Setenv("TF_HOSTNAME_KEY_NAME", "name")
	exitCode := cmdList(&stdout, &stderr, &s)
	os.Unsetenv("TF_HOSTNAME_KEY_NAME")
	assert.Equal(t, 0, exitCode)
	assert.Equal(t, "", stderr.String())

	// Decode the output to compare
	var act interface{}
	err = json.Unmarshal([]byte(stdout.String()), &act)
	assert.NoError(t, err)

	assert.Equal(t, exp, act)
}

func TestHostCommand(t *testing.T) {
	var s stateAnyTerraformVersion
	r := strings.NewReader(exampleStateFile)
	err := s.read(r)
	assert.NoError(t, err)

	assert.Equal(t, TerraformVersionPre0dot12, s.TerraformVersion)

	// Decode expectation as JSON
	var exp interface{}
	err = json.Unmarshal([]byte(expectedHostOneOutput), &exp)
	assert.NoError(t, err)

	// Run the command, capture the output
	var stdout, stderr bytes.Buffer
	exitCode := cmdHost(&stdout, &stderr, &s, "10.0.0.1")
	assert.Equal(t, 0, exitCode)
	assert.Equal(t, "", stderr.String())

	// Decode the output to compare
	var act interface{}
	err = json.Unmarshal([]byte(stdout.String()), &act)
	assert.NoError(t, err)

	assert.Equal(t, exp, act)
}

func TestInventoryCommand(t *testing.T) {
	var s stateAnyTerraformVersion
	r := strings.NewReader(exampleStateFile)
	err := s.read(r)
	assert.NoError(t, err)

	assert.Equal(t, TerraformVersionPre0dot12, s.TerraformVersion)

	// Run the command, capture the output
	var stdout, stderr bytes.Buffer
	exitCode := cmdInventory(&stdout, &stderr, &s)
	assert.Equal(t, 0, exitCode)
	assert.Equal(t, "", stderr.String())

	assert.Equal(t, expectedInventoryOutput, stdout.String())
}

//
// Terraform 0.12 BEGIN
//

const exampleStateFileTerraform0dot12 = `
{
	"format_version": "0.1",
	"terraform_version": "0.12.1",
	"values": {
		"outputs": {
			"my_endpoint": {
				"sensitive": false,
				"value": "a.b.c.d.example.com"
			},
			"my_password": {
				"sensitive": true,
				"value": "1234"
			},
			"map": {
				"sensitive": false,
				"value": {
					"first": "a",
					"second": "b"
				}
			}
		},
		"root_module": {
			"resources": [
				{
					"address": "aws_instance.one",
					"type": "aws_instance",
					"name": "one",
					"provider_name": "aws",
					"schema_version": 1,
					"values": {
						"ami": "ami-00000000000000000",
						"id": "i-11111111111111111",
						"private_ip": "10.0.0.1",
						"public_ip": "35.159.25.34",
						"tags": {
							"Name": "one-aws-instance"
						},
						"tags_all": {
							"Additional": "another-tag"
						},
						"volume_tags": {
							"Ignored": "stuff"
						}
					}
				},
				{
				  "address": "aws_route53_record.this",
				  "mode": "managed",
				  "type": "aws_route53_record",
				  "name": "this",
				  "index": "testb",
				  "provider_name": "aws",
				  "schema_version": 2,
				  "values": {
					"alias": [],
					"allow_overwrite": null,
					"failover_routing_policy": [],
					"fqdn": "testb.tv",
					"geolocation_routing_policy": [],
					"health_check_id": null,
					"id": "abc.tv._A",
					"latency_routing_policy": [],
					"multivalue_answer_routing_policy": null,
					"name": "testb.tv",
					"records": [
					  "2.2.2.2"
					],
					"set_identifier": null,
					"ttl": 300,
					"type": "A",
					"weighted_routing_policy": [],
					"zone_id": "abc"
				  }
				},
				{
					"address": "vsphere_tag.bar",
					"mode": "managed",
					"type": "vsphere_tag",
					"name": "bar",
					"provider_name": "vsphere",
					"schema_version": 0,
					"values": {
						"category_id": "urn:vmomi:InventoryServiceCategory:dc032379-bc2c-4fe5-bd8a-77040e3f4bc8:GLOBAL",
						"description": "",
						"id": "urn:vmomi:InventoryServiceTag:c70f4a73-f744-458a-b2ef-595e3c7c7c28:GLOBAL",
						"name": "bar"
					},
					"depends_on": [
						"vsphere_tag_category.foo"
					]
				},
				{
					"address": "vsphere_tag_category.foo",
					"mode": "managed",
					"type": "vsphere_tag_category",
					"name": "foo",
					"provider_name": "vsphere",
					"schema_version": 0,
					"values": {
						"associable_types": [
							"VirtualMachine"
						],
						"cardinality": "SINGLE",
						"description": "",
						"id": "urn:vmomi:InventoryServiceCategory:dc032379-bc2c-4fe5-bd8a-77040e3f4bc8:GLOBAL",
						"name": "foo"
					}
				},
				{
					"address": "vsphere_virtual_machine.vm",
					"mode": "managed",
					"type": "vsphere_virtual_machine",
					"name": "vm",
					"provider_name": "vsphere",
					"schema_version": 3,
					"values": {
						"alternate_guest_name": "",
						"annotation": "",
						"boot_delay": 0,
						"boot_retry_delay": 10000,
						"boot_retry_enabled": false,
						"cdrom": [],
						"change_version": "2019-08-24T17:27:59.706242Z",
						"clone": [],
						"cpu_hot_add_enabled": false,
						"cpu_hot_remove_enabled": false,
						"cpu_limit": -1,
						"cpu_performance_counters_enabled": false,
						"cpu_reservation": 0,
						"cpu_share_count": 4000,
						"cpu_share_level": "normal",
						"custom_attributes": {},
						"datastore_cluster_id": null,
						"datastore_id": "datastore-1",
						"default_ip_address": "12.34.56.78",
						"disk": [
							{
								"attach": false,
								"datastore_id": "datastore-1",
								"device_address": "scsi:0:0",
								"disk_mode": "persistent",
								"disk_sharing": "sharingNone",
								"eagerly_scrub": false,
								"io_limit": -1,
								"io_reservation": 0,
								"io_share_count": 1000,
								"io_share_level": "normal",
								"keep_on_remove": true,
								"key": 2000,
								"label": "disk0",
								"name": "",
								"path": "foo/bar.vmdk",
								"size": 4,
								"thin_provisioned": true,
								"unit_number": 0,
								"uuid": "6000C292-6cff-cc74-87e5-37ce78a22b57",
								"write_through": false
							}
						],
						"efi_secure_boot_enabled": false,
						"enable_disk_uuid": false,
						"enable_logging": false,
						"ept_rvi_mode": "automatic",
						"extra_config": {},
						"firmware": "bios",
						"folder": "",
						"force_power_off": true,
						"guest_id": "debian8_64Guest",
						"guest_ip_addresses": [
							"12.34.56.78"
						],
						"host_system_id": "host-764",
						"hv_mode": "hvAuto",
						"id": "42361f05-2e60-752c-5999-6c592f0a3904",
						"ignored_guest_ips": null,
						"imported": false,
						"latency_sensitivity": "normal",
						"memory": 2048,
						"memory_hot_add_enabled": false,
						"memory_limit": -1,
						"memory_reservation": 0,
						"memory_share_count": 20480,
						"memory_share_level": "normal",
						"migrate_wait_timeout": 30,
						"moid": "vm-827",
						"name": "vm",
						"nested_hv_enabled": false,
						"network_interface": [
							{
								"adapter_type": "vmxnet3",
								"bandwidth_limit": -1,
								"bandwidth_reservation": 0,
								"bandwidth_share_count": 100,
								"bandwidth_share_level": "high",
								"device_address": "pci:0:7",
								"key": 4000,
								"mac_address": "00:50:56:b3:af:02",
								"network_id": "dvportgroup-837",
								"use_static_mac": false
							}
						],
						"num_cores_per_socket": 1,
						"num_cpus": 4,
						"reboot_required": false,
						"resource_pool_id": "resgroup-768",
						"run_tools_scripts_after_power_on": true,
						"run_tools_scripts_after_resume": true,
						"run_tools_scripts_before_guest_reboot": false,
						"run_tools_scripts_before_guest_shutdown": true,
						"run_tools_scripts_before_guest_standby": true,
						"scsi_bus_sharing": "noSharing",
						"scsi_controller_count": 1,
						"scsi_type": "pvscsi",
						"shutdown_wait_timeout": 3,
						"swap_placement_policy": "inherit",
						"sync_time_with_host": false,
						"tags": [
							"urn:vmomi:InventoryServiceTag:c70f4a73-f744-458a-b2ef-595e3c7c7c28:GLOBAL"
						],
						"uuid": "42361f05-2e60-752c-5999-6c592f0a3904",
						"vapp": [],
						"vapp_transport": [],
						"vmware_tools_status": "guestToolsRunning",
						"vmx_path": "foo/bar.vmx",
						"wait_for_guest_ip_timeout": 0,
						"wait_for_guest_net_routable": true,
						"wait_for_guest_net_timeout": 5
					},
					"depends_on": [
						"vsphere_tag.bar"
					]
				}
			],
			"child_modules": [
				{
					"resources": [
						{
							"address": "aws_instance.host",
							"type": "aws_instance",
							"name": "host",
							"values": {
								"ami": "ami-00000000000000001",
								"id": "i-22222222222222222",
								"private_ip": "10.0.0.2",
								"public_ip": "",
								"tags": {
									"Name": "two-aws-instance"
								}
							}
						}
					],
					"address": "module.my-module-two"
				},
				{
					"resources": [
						{
							"address": "aws_instance.host",
							"type": "aws_instance",
							"name": "host",
							"index": 0,
							"values": {
								"ami": "ami-00000000000000001",
								"id": "i-33333333333333333",
								"private_ip": "10.0.0.3",
								"public_ip": "",
								"tags": {
									"Name": "three-aws-instance"
								}
							}
						},
						{
							"address": "aws_instance.host",
							"type": "aws_instance",
							"name": "host",
							"index": 1,
							"values": {
								"ami": "ami-00000000000000001",
								"id": "i-11133333333333333",
								"private_ip": "10.0.1.3",
								"public_ip": "",
								"tags": {
									"Name": "three-aws-instance"
								}
							}
						}
					],
					"address": "module.my-module-three"
				},
				{
					"resources": [
						{
							"address": "aws_instance.host",
							"type": "aws_instance",
							"name": "host",
							"index": "for_each_example.first",
							"values": {
								"ami": "ami-00000000000000001",
								"id": "i-44444444444444444",
								"private_ip": "10.0.0.4",
								"public_ip": "",
								"tags": {
									"Name": "four-aws-instance"
								}
							}
						},
						{
							"address": "aws_instance.host",
							"type": "aws_instance",
							"name": "host",
							"index": "for_each_example.second",
							"values": {
								"ami": "ami-00000000000000001",
								"id": "i-11144444444444444",
								"private_ip": "10.0.1.4",
								"public_ip": "",
								"tags": {
									"Name": "four-aws-instance"
								}
							}
						}
					],
					"address": "module.my-module-four"
				}
			]
		}
	}
}
`

const expectedListOutputTerraform0dot12 = `
{
	"all": {
		"hosts": [
			"10.0.0.2",
			"10.0.0.3",
			"10.0.0.4",
			"10.0.1.3",
			"10.0.1.4",
			"12.34.56.78",
			"35.159.25.34"
		],
		"vars": {
			"my_endpoint": "a.b.c.d.example.com",
			"my_password": "1234",
			"map": {"first": "a", "second": "b"}
		}
	},
	"one_0": ["35.159.25.34"],
	"one": ["35.159.25.34"],
	"module_my-module-four_host_for_each_example_first": ["10.0.0.4"],
	"module_my-module-four_host_for_each_example_second": ["10.0.1.4"],
	"module_my-module-four_host": ["10.0.0.4", "10.0.1.4"],
	"module_my-module-two_host_0": ["10.0.0.2"],
	"module_my-module-two_host": ["10.0.0.2"],
	"module_my-module-three_host_0": ["10.0.0.3"],
	"module_my-module-three_host_1": ["10.0.1.3"],
	"module_my-module-three_host": ["10.0.0.3", "10.0.1.3"],

	"type_aws_instance": ["10.0.0.2", "10.0.0.3", "10.0.0.4", "10.0.1.3", "10.0.1.4", "35.159.25.34"],

	"name_four-aws-instance": ["10.0.0.4", "10.0.1.4"],
	"name_one-aws-instance": ["35.159.25.34"],
	"name_two-aws-instance": ["10.0.0.2"],
	"name_three-aws-instance": ["10.0.0.3", "10.0.1.3"],

	"additional_another-tag": ["35.159.25.34"],

	"foo_bar": ["12.34.56.78"],
	"type_vsphere_virtual_machine": ["12.34.56.78"],
	"vm_0": ["12.34.56.78"],
	"vm": ["12.34.56.78"]
}
`

const expectedInventoryOutputTerraform0dot12 = `[additional_another-tag]
35.159.25.34

[all]
10.0.0.2
10.0.0.3
10.0.0.4
10.0.1.3
10.0.1.4
12.34.56.78
35.159.25.34

[all:vars]
map={"first":"a","second":"b"}
my_endpoint="a.b.c.d.example.com"
my_password="1234"

[foo_bar]
12.34.56.78

[module_my-module-four_host]
10.0.0.4
10.0.1.4

[module_my-module-four_host_for_each_example_first]
10.0.0.4

[module_my-module-four_host_for_each_example_second]
10.0.1.4

[module_my-module-three_host]
10.0.0.3
10.0.1.3

[module_my-module-three_host_0]
10.0.0.3

[module_my-module-three_host_1]
10.0.1.3

[module_my-module-two_host]
10.0.0.2

[module_my-module-two_host_0]
10.0.0.2

[name_four-aws-instance]
10.0.0.4
10.0.1.4

[name_one-aws-instance]
35.159.25.34

[name_three-aws-instance]
10.0.0.3
10.0.1.3

[name_two-aws-instance]
10.0.0.2

[one]
35.159.25.34

[one_0]
35.159.25.34

[type_aws_instance]
10.0.0.2
10.0.0.3
10.0.0.4
10.0.1.3
10.0.1.4
35.159.25.34

[type_vsphere_virtual_machine]
12.34.56.78

[vm]
12.34.56.78

[vm_0]
12.34.56.78

`

const expectedHostOneOutputTerraform0dot12 = `
{
	"ami": "ami-00000000000000000",
	"ansible_host": "35.159.25.34",
	"id":"i-11111111111111111",
	"private_ip":"10.0.0.1",
	"public_ip": "35.159.25.34",
	"tags.#": "1",
	"tags.Name": "one-aws-instance",
	"tags_all.#": "1",
	"tags_all.Additional": "another-tag",
	"volume_tags.#":"1",
	"volume_tags.Ignored":"stuff"
}
`

func TestListCommandTerraform0dot12(t *testing.T) {
	var s stateAnyTerraformVersion
	r := strings.NewReader(exampleStateFileTerraform0dot12)
	err := s.read(r)
	assert.NoError(t, err)

	assert.Equal(t, TerraformVersion0dot12, s.TerraformVersion)

	// Decode expectation as JSON
	var exp interface{}
	err = json.Unmarshal([]byte(expectedListOutputTerraform0dot12), &exp)
	assert.NoError(t, err)

	// Run the command, capture the output
	var stdout, stderr bytes.Buffer
	exitCode := cmdList(&stdout, &stderr, &s)
	assert.Equal(t, 0, exitCode)
	assert.Equal(t, "", stderr.String())

	// Decode the output to compare
	var act interface{}
	err = json.Unmarshal([]byte(stdout.String()), &act)
	assert.NoError(t, err)

	assert.Equal(t, exp, act)
}

func TestHostCommandTerraform0dot12(t *testing.T) {
	var s stateAnyTerraformVersion
	r := strings.NewReader(exampleStateFileTerraform0dot12)
	err := s.read(r)
	assert.NoError(t, err)

	assert.Equal(t, TerraformVersion0dot12, s.TerraformVersion)

	// Decode expectation as JSON
	var exp interface{}
	err = json.Unmarshal([]byte(expectedHostOneOutputTerraform0dot12), &exp)
	assert.NoError(t, err)

	// Run the command, capture the output
	var stdout, stderr bytes.Buffer
	exitCode := cmdHost(&stdout, &stderr, &s, "35.159.25.34")
	assert.Equal(t, 0, exitCode)
	assert.Equal(t, "", stderr.String())

	// Decode the output to compare
	var act interface{}
	err = json.Unmarshal([]byte(stdout.String()), &act)
	assert.NoError(t, err)

	assert.Equal(t, exp, act)
}

func TestInventoryCommandTerraform0dot12(t *testing.T) {
	var s stateAnyTerraformVersion
	r := strings.NewReader(exampleStateFileTerraform0dot12)
	err := s.read(r)
	assert.NoError(t, err)

	assert.Equal(t, TerraformVersion0dot12, s.TerraformVersion)

	// Run the command, capture the output
	var stdout, stderr bytes.Buffer
	exitCode := cmdInventory(&stdout, &stderr, &s)
	assert.Equal(t, 0, exitCode)
	assert.Equal(t, "", stderr.String())

	assert.Equal(t, expectedInventoryOutputTerraform0dot12, stdout.String())
}

//
// Terraform 0.12 END
//
07070100000016000081A400000000000000000000000161046BE5000006E0000000000000000000000000000000000000004000000000ansible-terraform-inventory-0.10/parser_test.go.example.tfstate{
	"format_version": "0.1",
	"terraform_version": "0.12.1",
	"values": {
		"outputs": {
			"my_endpoint": {
				"sensitive": false,
				"value": "a.b.c.d.example.com"
			},
			"my_password": {
				"sensitive": true,
				"value": "1234"
			},
			"map": {
				"sensitive": false,
				"value": {
					"first": "a",
					"second": "b"
				}
			}
		},
		"root_module": {
			"resources": [
				{
					"address": "aws_instance.one",
					"type": "aws_instance",
					"name": "one",
					"provider_name": "aws",
					"schema_version": 1,
					"values": {
						"ami": "ami-00000000000000000",
						"id": "i-11111111111111111",
						"private_ip": "10.0.0.1",
						"public_ip": "35.159.25.34",
						"tags": {
							"Name": "one-aws-instance",
						},
						"tags_all": {
							"Additional": "another-tag"
						},
						"volume_tags": {
							"Ignored": "stuff"
						}
					}
				}
			],
			"child_modules": [
				{
					"resources": [
						{
							"address": "aws_instance.host",
							"type": "aws_instance",
							"name": "host",
							"values": {
								"ami": "ami-00000000000000001",
								"id": "i-22222222222222222",
								"private_ip": "10.0.0.2",
								"public_ip": "",
								"tags": {
									"Name": "two-aws-instance"
								}
							}
						}
					],
					"address": "module.my-module-two"
				},
				{
					"resources": [
						{
							"address": "aws_instance.host",
							"type": "aws_instance",
							"name": "host",
							"values": {
								"ami": "ami-00000000000000001",
								"id": "i-33333333333333333",
								"private_ip": "10.0.0.3",
								"public_ip": "",
								"tags": {
									"Name": "three-aws-instance"
								}
							}
						}
					],
					"address": "module.my-module-three"
				}
			]
		}
	}
}
07070100000017000041ED00000000000000000000000261046BE500000000000000000000000000000000000000000000002500000000ansible-terraform-inventory-0.10/pkg07070100000018000081A400000000000000000000000161046BE500000000000000000000000000000000000000000000002E00000000ansible-terraform-inventory-0.10/pkg/.gitkeep07070100000019000081A400000000000000000000000161046BE5000022CF000000000000000000000000000000000000002D00000000ansible-terraform-inventory-0.10/resource.gopackage main

import (
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"
)

// keyNames contains the names of the keys to check for in each resource in the
// state file. This allows us to support multiple types of resource without too
// much fuss.
var keyNames []string
var nameParser *regexp.Regexp

func init() {
	keyNames = []string{
		"ipv4_address",                     // DO and SoftLayer and HetznerCloud
		"public_ip",                        // AWS
		"public_ipv6",                      // Scaleway
		"ipaddress",                        // CS
		"primary_ip",                                          // Profitbricks, Cherry servers
		"ip_address",                       // VMware, Docker, Linode
		"private_ip",                       // AWS
		"network_interface.0.ipv4_address", // VMware
		"default_ip_address",               // provider.vsphere v1.1.1
		"access_ip_v4",                     // OpenStack
		"floating_ip",                      // OpenStack
		"network_interface.0.access_config.0.nat_ip",          // GCE
		"network_interface.0.access_config.0.assigned_nat_ip", // GCE
		"network_interface.0.address",                         // GCE
		"ipv4_address_private",                                // SoftLayer
		"networks.0.ip4address",                               // Exoscale
		"primaryip",                                           // Joyent Triton
		"network_interface.0.addresses.0",                     // Libvirt
		"network.0.address",                                   // Packet
		"nic_list.0.ip_endpoint_list.0.ip",                    // Nutanix
		"network_interface.0.nat_ip_address",                  // Yandex
		"network_interface.0.ip_address",                      // Yandex
		"default_ipv4_address",                                // Telmate/Proxmox
		"ssh_host",                                            // Telmate/Proxmox
	}

	// Formats:
	// - type.[module_]name (no `count` attribute; contains module name if we're not in the root module)
	// - type.[module_]name.0 (if resource has `count` attribute)
	// - type.[module_]name.resource_name
	// - "data." prefix should not parse and be ignored by caller (does not represent a host)
	nameParser = regexp.MustCompile(`^([\w\-]+)\.([\w\-]+)(?:\.(\d+|[\S+]+))?$`)
}

type Resource struct {

	// The state (as unmarshalled from the statefile) which this resource wraps.
	// Everything which Terraform knows about the resource can be found in here.
	State resourceState

	// The key name of the resource, provided to the constructor. Unfortunately,
	// it seems like the counter index can only be found here.
	keyName string

	// Extracted from keyName
	resourceType string
	baseName     string

	// counterNumeric is 0 for resources created without `count=` attribute or
	// having a non-numeric string index
	counterNumeric int
	// counterStr is set if the resource index (e.g. in `for_each`-constructed
	// resources) is not a number.
	counterStr string
}

func NewResource(keyName string, state resourceState) (*Resource, error) {
	m := nameParser.FindStringSubmatch(keyName)

	// This should not happen unless our regex changes.
	if len(m) != 4 {
		return nil, fmt.Errorf("couldn't parse resource keyName: %s", keyName)
	}

	counterNumeric := 0
	counterStr := ""
	var err error
	if m[3] != "" {
		// The third section should be the index, if it's present. Not sure what
		// else we can do other than panic (which seems highly undesirable) if that
		// isn't the case. With Terraform 0.12 for_each syntax, index can also be
		// a non-numeric string (loop over any string value).
		counterNumeric, err = strconv.Atoi(m[3])
		if err != nil {
			counterNumeric = 0
			counterStr = m[3]
		}
	}

	return &Resource{
		State:          state,
		keyName:        keyName,
		resourceType:   m[1],
		baseName:       m[2],
		counterNumeric: counterNumeric,
		counterStr:     counterStr,
	}, nil
}

func (r Resource) IsSupported() bool {
	return r.Address() != ""
}

// Tags returns a map of arbitrary key/value pairs explicitly associated with
// the resource. Different providers have different mechanisms for attaching
// these.
func (r Resource) Tags() map[string]string {
	t := map[string]string{}

	switch r.resourceType {
	case "openstack_compute_instance_v2":
		for k, v := range r.Attributes() {
			parts := strings.SplitN(k, ".", 2)
			// At some point Terraform changed the key for counts of attributes to end with ".%"
			// instead of ".#". Both need to be considered as Terraform still supports state
			// files using the old format.
			if len(parts) == 2 && parts[0] == "tag" && parts[1] != "#" && parts[1] != "%" {
				kk := strings.ToLower(parts[1])
				vv := strings.ToLower(v)
				t[kk] = vv
			} else if len(parts) == 2 && parts[0] == "metadata" && parts[1] != "#" && parts[1] != "%" {
				kk := strings.ToLower(parts[1])
				vv := strings.ToLower(v)
				t[kk] = vv
			}
		}
	case "opentelekomcloud_compute_instance_v2":
		for k, v := range r.Attributes() {
			parts := strings.SplitN(k, ".", 2)
			// At some point Terraform changed the key for counts of attributes to end with ".%"
			// instead of ".#". Both need to be considered as Terraform still supports state
			// files using the old format.
			if len(parts) == 2 && parts[0] == "tag" && parts[1] != "#" && parts[1] != "%" {
				kk := strings.ToLower(parts[1])
				vv := strings.ToLower(v)
				t[kk] = vv
			} else if len(parts) == 2 && parts[0] == "metadata" && parts[1] != "#" && parts[1] != "%" {
				kk := strings.ToLower(parts[1])
				vv := strings.ToLower(v)
				t[kk] = vv
			}
		}
	case "aws_instance", "linode_instance":
		for k, v := range r.Attributes() {
			parts := strings.SplitN(k, ".", 2)
			// At some point Terraform changed the key for counts of attributes to end with ".%"
			// instead of ".#". Both need to be considered as Terraform still supports state
			// files using the old format.
			if len(parts) == 2 && (parts[0] == "tags" || parts[0] == "tags_all") && parts[1] != "#" && parts[1] != "%" {
				kk := strings.ToLower(parts[1])
				vv := strings.ToLower(v)
				t[kk] = vv
			}
		}
	case "aws_spot_instance_request":
		for k, v := range r.Attributes() {
			parts := strings.SplitN(k, ".", 2)
			// At some point Terraform changed the key for counts of attributes to end with ".%"
			// instead of ".#". Both need to be considered as Terraform still supports state
			// files using the old format.
			if len(parts) == 2 && (parts[0] == "tags" || parts[0] == "tags_all") && parts[1] != "#" && parts[1] != "%" {
				kk := strings.ToLower(parts[1])
				vv := strings.ToLower(v)
				t[kk] = vv
			}
		}
	case "vsphere_virtual_machine":
		for k, v := range r.Attributes() {
			parts := strings.SplitN(k, ".", 2)

			if len(parts) == 2 && parts[0] == "custom_configuration_parameters" && parts[1] != "#" && parts[1] != "%" {
				kk := strings.ToLower(parts[1])
				vv := strings.ToLower(v)
				t[kk] = vv
			}
			if len(parts) == 2 && parts[0] == "tags" && parts[1] != "#" && parts[1] != "%" {
				kk := strings.ToLower(parts[1])
				vv := strings.ToLower(v)
				t[kk] = vv
			}
		}
	case "digitalocean_droplet", "google_compute_instance", "scaleway_server":
		for k, v := range r.Attributes() {
			parts := strings.SplitN(k, ".", 2)
			if len(parts) == 2 && parts[0] == "tags" && parts[1] != "#" {
				vv := strings.ToLower(v)
				t[vv] = ""
			}
		}
	case "triton_machine", "exoscale_compute":
		for k, v := range r.Attributes() {
			parts := strings.SplitN(k, ".", 2)
			if len(parts) == 2 && parts[0] == "tags" && parts[1] != "%" {
				kk := strings.ToLower(parts[1])
				vv := strings.ToLower(v)
				t[kk] = vv
			}
		}
	case "yandex_compute_instance", "hcloud_server":
		for k, v := range r.Attributes() {
			parts := strings.SplitN(k, ".", 2)
			// At some point Terraform changed the key for counts of attributes to end with ".%"
			// instead of ".#". Both need to be considered as Terraform still supports state
			// files using the old format.
			if len(parts) == 2 && parts[0] == "labels" && parts[1] != "#" && parts[1] != "%" {
				kk := strings.ToLower(parts[1])
				vv := strings.ToLower(v)
				t[kk] = vv
			}
		}
	}

	return t
}

// Attributes returns a map containing everything we know about this resource.
func (r Resource) Attributes() map[string]string {
	return r.State.Primary.Attributes
}

// Hostname returns the hostname of this resource.
func (r Resource) Hostname() string {
	if keyName := os.Getenv("TF_HOSTNAME_KEY_NAME"); keyName != "" {
		if ip := r.State.Primary.Attributes[keyName]; ip != "" {
			return ip
		}
	}

	return r.Address()
}

// Address returns the IP address of this resource.
func (r Resource) Address() string {
	if keyName := os.Getenv("TF_KEY_NAME"); keyName != "" {
		if ip := r.State.Primary.Attributes[keyName]; ip != "" {
			return ip
		}
	} else {
		for _, key := range keyNames {
			if ip := r.State.Primary.Attributes[key]; ip != "" {
				return ip
			}
		}
	}

	return ""
}
0707010000001A000081A400000000000000000000000161046BE500000194000000000000000000000000000000000000002C00000000ansible-terraform-inventory-0.10/version.gopackage main

// Deliberately uninitialized. See below.
var build_version string

// versionInfo returns a string containing the version information of the
// current build. It's empty by default, but can be included as part of the
// build process by setting the main.build_version variable.
func versionInfo() string {
	if build_version != "" {
		return build_version
	} else {
		return "unknown"
	}
}
0707010000001B000081A400000000000000000000000161046BE5000000DE000000000000000000000000000000000000003100000000ansible-terraform-inventory-0.10/version_test.gopackage main

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestVersionInfo(t *testing.T) {
	assert.Equal(t, "unknown", versionInfo())

	build_version = "vXYZ"
	assert.Equal(t, "vXYZ", versionInfo())
}
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!189 blocks
openSUSE Build Service is sponsored by