Open Lasering opened 6 years ago
Hi @Lasering,
If I understand your use case correctly, this touches on one of the reasons for Local Values. I would suggest the following alternative configuration:
variable "security-group-id" {
description = "The security group id to which the rules should be added"
}
locals {
network-ipv4-cidr = "169.254.216.0/24"
}
output "network-ipv4-cidr" {
value = "${local.network-ipv4-cidr}"
}
resource "openstack_networking_secgroup_rule_v2" "ssh-network-ipv4" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 22
port_range_max = 22
remote_ip_prefix = "${local.network-ipv4-cidr}"
security_group_id = "${var.security-group-id}"
}
That solves the problem. However I still think it would be simpler to allow using the output variable directly.
Hi @Lasering,
Thanks for that feedback! At the moment we do not plan to make outputs directly interpolatable, since there is already a working approach as James noted and so we're currently focusing our efforts elsewhere, but once the current configuration language revamp work is stable (which includes a lot of changes already) we can revisit this and figure out the tradeoffs for this feature: increased convenience of not needing to separately define a local value vs. potential increase in user-facing complexity of having another possible way to say the same thing and confusing the idea that outputs are for passing data out of a module.
For the moment, I'm going to tag this as "thinking" to remind us to revisit it once other work has settled down. Thanks again!
Just to chime in, I have the same need, but with a computed property (resource ID) - in such case, the local
approach doesn't work, as that's not a user-settable value.
If you want to use a computed property, just use the computed property. If you need to output it as well, output it.
You can solve it in a very odd, a bit funny, but totally working way.
Here we declare something
output in module x
# modules/x/something.tf
output "something" {
value = "nothing"
}
In this place we will use output something
using null_resource
trigger (could be any other resource/locals you ever need)
# modules/x/need_something.tf
variable "something" {
}
resource "null_resource" "debug_stuff" {
triggers {
something = "${var.something}"
}
}
And now the best part of it, how to wire all these up in root.tf
# root.tf
module "x" {
source = "./modules/x"
something = "${module.x.something}"
}
Voila!
Hi @nkbt! Thanks for sharing that interesting discovery.
What you've found there is an interesting consequence of an intentional design feature of Terraform modules: each input variable and output value participates separately in the dependency graph. Terraform is designed this way to allow better composability of modules that may have dependencies between one another, and to achieve better concurrency, but as you've seen here it also means that you can have an input variable that directly depends on an output value, as long as there are no graph cycles otherwise.
Of course, this is more code than just defining a local value to use for both the output and the other references:
locals {
something = "nothing"
}
resource "null_resource" "debug_stuff" {
triggers = {
something = "${local.something}"
}
}
output "something" {
value = "${local.something}"
}
...so it seems like there's little reason to do this in practice, but it's definitely an interesting unintended outcome of the design of modules!
@apparentlymart I totally get the locals
are meant to be used for this. In our specific case we have hundreds of modules that were created before locals
existed. We also interop tf with some other tooling by examining tf state after it completed plan/apply cycle. The problem with recent tf is that it omits outputs that are not used from the state (not used in tf's opinion), but we do rely on them to pull the data out to be used for other tooling. So after upgrading to the most recent tf we needed some automated solution to force those output
values to be preserved in the state (hence null_resource) but at the same time we do not want to rewrite hundreds of existing modules to use locals
.
Since al out tf templates are basically erb
templates, on processing step I inject a pair of variable/null_resource trigger to ensure it stays in state. And then add reference to it root.tf.json
for the module that needs it.
I guess our own context is quite irrelevant for most of tf users, but we went that erb preprocessing, tf state parsing and post-tf tooling route because we were using terraform since v0.4 (or smth like that) and most of the existing functionality did not exist at the time (hell, it was rough 4 years ago). But we still run all the stuff in prod and do not want to add more changes to the state/*.tf then it is absolutely essential.
one of my modules depends on a computed property of the current module. How do I do this?
@CodeSwimBikeRun store computed property as one of locals
and re-use it within module
Hello, I'm trying to use a couple of the suggestions but still unable to make it work (my issue is probably very similar to the one posed by CodeSwimBikeRun). The variable of interest is the MAC address -for a second network card- that is computed via an output variable called MYMAC_ADDRESS. The first thing is that I have to declare MYMAC_ADDRESS among the variables or get the message: "Error: resource 'null_resource.copy_in_setup_data_mgmt' provisioner remote-exec (#8): unknown variable referenced: 'MYMAC_ADDRESS'; define it with a 'variable' block." Once this is done, the first part of the TF script seems to work correctly as the apply command returns the right MAC value.
What is not working is the second part of the script where inline commands do not recognize MYMAC_ADDRESS correctly . To sum things up: -If no locals are defined and the inline command calls $(var.MYMAC_ADDRESS) --> The deployment completes without error messages but the inline commands have used the 'default' value for MYMAC_ADDRESS rather than the computed MAC address. This makes me wonder if some how TF is still seeing them as different variables. -When I define MYMAC_ADDRESS as a local variable and tried following nkbt's suggestion using the example from jbardin, I get the following error message: "* null_resource.copy_in_setup_data_mgmt[1]: At column 1, line 1: output of an HIL expression must be a string, or a single list (argument 2 is TypeList) in:"
Thanks. P.S. Just for clarification, I haven't defined any module and would expect Terraform to treat everything as a single one.
Hi,
I want to use s3-bucket-arn in lambda module. s3 and lambda having different modules.
structure like below -main.tf -variable.tf -values.tfvars -/modules
How this would be possible?
can anyone help me. Thanks
Hi all,
If you have usage questions about Terraform as it exists today, rather than discussion about this feature request, please create a topic in the community forum. We use GitHub issues only for tracking potential future work, and asking questions here creates notification noise for those who are watching this issue to see development updates relating to it.
any updates on this issue, can we use outputs in same module? like below
resource "azurerm_servicebus_namespace" "sb_namespace" {
name = "${local.prefix}-${local.environment_tag}-sbnamespace"
location = local.resource_location
resource_group_name = local.resource_group_name
sku = "Standard"
tags = {
environment = local.environment_tag
}
}
output "sb_namespace_name" {
value = azurerm_servicebus_namespace.sb_namespace.name
description = "The name of the azure servicebus namespace being deployed"
}
resource "azurerm_servicebus_namespace_authorization_rule" "sb_authrule" {
name = "${local.prefix}-sbnauth"
namespace_name = var.sb_namespace_name
resource_group_name = output.resource_group_name
send = true
listen = true
manage = true
depends_on = [
output.sb_namespace_name,
]
}
Error: A managed resource "output" "sb_namespace_name" has not been declared in the root module.
The recommended answer is still to declare a named local value if you need to use a complex expression in more than one location in a module.
That is the Terraform equivalent of what in a general-purpose language might be a local variable used both in a call to some other function and in the return value:
let ret = 2;
doSomething(ret);
return ret;
locals {
ret = 2
}
resource "something" "example" {
value = local.ret
}
output "example" {
value = local.ret
}
does that mean "output" is only meant to be used when u want something out from another module?
In my scenario of using the preferred approach of a local variable, the value is an object which contains a key:value dependent on an attribute of resource in the root module. After the resource creation, this local variable would then be passed into another module for subsequent use.
Since local variables are instantiated before resources, the above won't work as it is a chicken/egg situation. I.e., How do I declare an attribute of a resource that has yet to be created?
Since output variables aren't recognized by the root module, it is therefore not possible to pass around an attribute of a resource within a local variable. However, the attribute is accessible when referring to the resource directly!
I recognize that it's possible that I'm working with an anti-pattern or I'm failing to understand something obvious, but not being able to use attributes of resources in a local variable seems like a fairly large constraint. Is there a built-in that I'm not aware of that I can use to overcome this? If not, having a feature that allows for this capability seems reasonable to me! ☺️
So there is no way to reference output in the module itself. One needs to define locals
and duplicated name in outputs
if want "same" entity...
So there is no way to reference output in the module itself. One needs to define locals and duplicated name in outputs if want "same" entity...
Yes. Some would consider this unfortunate, and that's understandable.
IMHO, to keep clean/readable code, I am putting a lot of things in locals {}
, including all outputs, and then defining outputs and other attributes from local values.
Terraform Version
Terraform Configuration Files
Crash Output
Expected Behavior
Be able to use output variables inside the module they are defined in. I suggest using the syntax
${output.output-var-name}
because using${var.output-var-name}
will colide with a variable defined with the same name.Additional Context
This piece of code is defined in a very simple module so it can be reused on the modules that actually instantiate a service. By defining an output variable with an hardcoded value we hoped to use it inside the module and at the same time output it because it is also useful for the module that will use this module to have access to it.
I know I could have define a variable and an output variable both with the same name. Then just assign the output variable value to the value of the variable. However that would convey the message to the users of my module that they can parameterize it, which I not something I want to allow!