starhawking / python-terrascript

Create Terraform files using Python scripts.
BSD 2-Clause "Simplified" License
516 stars 76 forks source link

Accessing attributes inside a Terrascript configuration #23

Open mjuenema opened 6 years ago

mjuenema commented 6 years ago

Nitish Kumar Yadav on LinkedIn Hi Markus, Needed one help, I have a terraform and need to convert that to terrascript, but facing problems as the resources are having nested variables

provider "aws" {
    access_key = "AWS_ACCESS_KEY"
    secret_key = "AWS_SECRET_KEY"
    region = "ap-south-1"
}

resource "aws_key_pair" "test_keypair" {
    key_name="id_rsa.pub"
    public_key = "${file("/home/cloud_user/.ssh/id_rsa.pub")}"
}

resource "aws_instance" "test_instance" {
    ami = "ami-2ed19c41"
    instance_type = "t2.micro"
    key_name= "id_rsa.pub"

    connection {
        user = "ec2-user"
        private_key = "${file("/home/cloud_user/.ssh/id_rsa")}"
    }

    provisioner "remote-exec" {
        inline = [
            "touch ~/provisioned",
            "sudo yum install -y httpd"
        ]
    }

    tags {
        Name = "Terraform provisioner test instance"
    }
}

output "public_ip" {
    value = "${aws_instance.test_instance.public_ip}"
}
import terrascript as ts
import terrascript.aws.r as r

ts.provider("aws", access_key="ACCESS_KEY", secret_key="SECRET_KEY", region="ap-south-1")

keypair = r.key_pair("test_keypair", key_name="id_rsa.pub",
                     public_key=ts.function.file("/home/cloud_user/.ssh/id_rsa.pub"))

conn = ts.connection(user="ec2_user",
                     private_key=ts.function.file("/home/cloud_user/.ssh/id_rsa"))

prov = ts.provisioner("remote-exec", inline=["touch ~/provisioned",
                                             "sudo yum install -y httpd"])

inst = r.instance("test_instance", ami="ami-2ed19c41", instance_type="t2.micro",
                  key_name="id_rsa.pub",
                  connection=conn, provisioner=prov,
                  tags={"Name": "Terraform provisioner test instance"})

ts.output("public_ip", value=inst.public_ip)

print(ts.dump())
austin-millan commented 6 years ago

Hi @mjuenema,

I believe this is a good place to ask this. I am trying to avoid assigning to local variables. Something like:

ts = Terrascript()
ts += d.vsphere_datacenter('dc1')
ts += d.vsphere_distributed_virtual_switch('dvs',
      name="",
      datacenter_id=ts.config.__getitem__('dc1'))

Ideally I would be able to reference data sources/resources already inside a Terrascript instance. I was looking into _Config__getitem__(key), but it isn't very straightforward to retrieve items.

Do you know of a way to achieve this currently, or should we always use local variables for this?

Thanks in advance!

mjuenema commented 6 years ago

Hi @austin-millan. I'd recommend to use Python variables to keep references to resources.

For the details of your question: the logic for referencing resources is implemented in the __repr__() method (calling the interpolated property) of the _base class in terrascript/__init__.py. But you should ignore all this and follow my advice below.

In release 0.5.1 I added the Terrascript.add() method so one can do this.

ts = Terrascript()
dc1 = ts.add(d.vsphere_datacenter('dc1'))
ts.addd.vsphere_distributed_virtual_switch('dvs',
      name="",
      datacenter_id=dc1)

The .add() method works like + but also returns its original argument, i.e. dc1 will be a "vSphere datacenter". It also looks nicer than +/+=, especially since I just learned that += is not supported by all Python implementations.

Unfortunately documentation is quite behind as I have very limited time at the moment to work on this project. It's very nice to see though that other people find python-terrascript useful, proven by the bug reports and questions I get :-)

mjuenema commented 4 years ago

I changed the title to better reflect the content of this issue.

mjuenema commented 4 years ago

Dot-notation as implemented in https://pypi.org/project/python-box/ may be a nice approach here. I'd probably have to re-implement this myself as I am already "abusing" the attribute access methods.

mjuenema commented 4 years ago

I just realised that at least for read-only access, dot-notation already works. See the test_datasource_attributes() method in https://github.com/mjuenema/python-terrascript/blob/develop/tests/test_data.py for an example.

Existing attributes are returned and accessing not (yet) existing ones creates a reference.

d = terrascript.data.aws_eip('external_ip', public_ip="1.2.3.4")
assert d.public_ip == "1.2.3.4"
assert d.unknown == 'data.aws_eip.external_ip.unknown'

Changing an existing attribute through dot-notation is not implemented but I am going to look into it.

mjuenema commented 4 years ago

TODO: Create a test case for Nitish's example.