starhawking / python-terrascript

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

Variable string concatenation #109

Open ilons opened 4 years ago

ilons commented 4 years ago

I'm trying to perform string concatenation with variables, but are having some trouble to get it to work.

Actual Terraform:

variable "env_prefix" {
  type  = string
  description = "my env prefix"
}
resource "azurerm_resource_group" "rg_demo" {
  name = "${var.env_prefix}-infra-demo"
  location = "North Europe"
}

Expected Terrascript:

import terrascript

script = terrascript.Terrascript()
env_prefix = script.add(terrascript.Variable("env_prefix", type="string", description="my env prefix"))
# Sorry for the convulated resource syntax, what is the proper way to do it?
script += type("azurerm_resource_group", (terrascript.Resource,), {})(
    "rg_demo",
    name=f"${{{env_prefix}}}-infra-demo",
    location="North Europe",
)

print(script)

Expected result:

{
  "variable": {
    "env_prefix": {
      "type": "string",
      "description": "my env prefix"
    }
  },
  "resource": {
    "azurerm_resource_group": {
      "rg_demo": {
        "name": "${var.env_prefix}-infra-demo",
        "location": "North Europe"
      }
    }
  }
}

Actual result:

{
  "variable": {
    "env_prefix": {
      "type": "string",
      "description": "my env prefix"
    }
  },
  "resource": {
    "azurerm_resource_group": {
      "rg_demo": {
        "name": "${{'type': 'string', 'description': 'my env prefix'}}-infra-demo",
        "location": "North Europe"
      }
    }
  }
}

Without string interpolation during the assignmen (name=env_prefix,, it gets almost right:

{
  "variable": {
    "env_prefix": {
      "type": "string",
      "description": "my env prefix"
    }
  },
  "resource": {
    "azurerm_resource_group": {
      "rg_demo": {
        "name": "var.env_prefix",
        "location": "North Europe"
      }
    }
  }
}

But should in fact be:

{
  "resource": {
    "azurerm_resource_group": {
      "rg_demo": {
        "name": "${var.env_prefix}",
        "location": "North Europe"
      }
    }
  }
}
ilons commented 4 years ago

Am I doing something wrong here, or is this simply a case which is not (yet) covered?

Using this class instead as Variable works:

import json

import terrascript

class Variable(terrascript.Variable):
    def __repr__(self):
        return f'var.{self._name}'

script = terrascript.Terrascript()
env_prefix = script.add(Variable("env_prefix", type="string", description="my env prefix"))
# Sorry for the convulated resource syntax, what is the proper way to do it?
script += type("azurerm_resource_group", (terrascript.Resource,), {})(
    "rg_demo",
    name=f"${{{env_prefix}}}-infra-demo",
    location="North Europe",
)

print(env_prefix)
print(json.dumps(env_prefix, indent=2))
print(script)

Actual output:

var.env_prefix
{
  "type": "string",
  "description": "my env prefix"
}
{
  "variable": {
    "env_prefix": {
      "type": "string",
      "description": "my env prefix"
    }
  },
  "resource": {
    "azurerm_resource_group": {
      "rg_demo": {
        "name": "${var.env_prefix}-infra-demo",
        "location": "North Europe"
      }
    }
  }
}

Is this change something that would be desirable that I make tests and a PR for in Terrascript?

ilons commented 4 years ago

Also related to this, should Variable be represented by var._name or ${var._name}? The later is what works when actually running the Terraform template, while the former requires you to manually surround it with ${}: name=f'${{{my_var}}}'.

ilons commented 4 years ago

Should be fixed in develop now.

ilons commented 4 years ago

Actually, this is not fixed. I think that variables should ALWAYS be inserted as ${var.name} instead of var.name in the json.

nielsonsantana commented 3 years ago

I had the same problem, testing terrascript - 0.9.0 with terraform - 0.13.6. Here my case.

import terrascript
import terrascript.resource
from terrascript import Output

def ref(r):
    return '${' + r + '}'

tf = terrascript.Terrascript()

tf += terrascript.terraform(
    required_providers={
        'tls': '~> 3.1.0'
    }
)

tls_private_key = terrascript.resource.tls_private_key('linkerd_trust_anchor_key', **{
    'algorithm': 'ECDSA',
    'ecdsa_curve': 'P256'
})
tf += tls_private_key

tf += Output('private_key_pem_without_brackets', value=tls_private_key.private_key_pem)
tf += Output('private_key_pem_with_brackets', value=ref(tls_private_key.private_key_pem))

In the output, the reference to a atribute of a resource or data, also must have a ${ }, not just variables. The output private_key_pem_without_brackets prints it's literal value and private_key_pem_with_brackets prints the private_key. I think that at the moment, the terrascript version 0.9.0 isn't usable without some tricks.


{
  "terraform": {
    "required_providers": {
      "tls": "~> 3.1.0"
    }
  },
  "resource": {
    "tls_private_key": {
      "linkerd_trust_anchor_key": {
        "algorithm": "ECDSA",
        "ecdsa_curve": "P256"
      }
    }
  },
  "output": {
    "private_key_pem_without_brackets": {
      "value": "tls_private_key.linkerd_trust_anchor_key.private_key_pem"
    },
    "private_key_pem_with_brackets": {
      "value": "${tls_private_key.linkerd_trust_anchor_key.private_key_pem}"
    }
  }
}