I am writing terraform script to create ASG on AWS. I tried to create it with terraform module to have a more reusable code. The problem is when I want to use variable from common-variable.tfvars on the module tf files, it keeps saying it is undefined and need to be declared. This way, the module will be less reuseable .
Here is an example
root
|
|___project 1
| |_____ main.tf
| |_____ common-variable.tfvars
|
|___ modules
|
|_____ a-module
|______ main.tf
So inside project 1 common-variable.tfvars, basically it looks like this
variable "a" {
description = "a variable"
default = "a"
}
variable "b" {
description = "a variable"
default = "b"
}
Inside a-module / main.tf looks like this
variable "name" {}
resource "aws_autoscaling_group" "asg-1" {
name = "${var.a}"
...
}
When I do terraform init, it says
resource 'aws_autoscaling_group.asg-1' config: unknown variable
referenced: 'a'. define it with 'variable' blocks
Any idea how i can use this common variable from within the module main .tf?
Update
I managed to pass terraform init by redeclaring the variable in each module. However, when i run terraform plan, this kind of error appears invalid value "common-variable.tfvars" for flag -var-file: multiple map declarations not supported for variables
Wrong tfvars format, should be key / value only, such as:
a = "a"
b = "b"
Second, check how do you refer the module, should be something like this:
source = "../modules/a-module"
You need to declare the variables required by the module within the module, and then pass them in when instantiating the module from your project.
Example stolen from hashicorp documentation
In your project:
module "assets_bucket" {
source = "./publish_bucket"
name = "assets"
}
module "media_bucket" {
source = "./publish_bucket"
name = "media"
}
In your module:
# publish_bucket/bucket-and-cloudfront.tf
variable "name" {} # this is the input parameter of the module
resource "aws_s3_bucket" "the_bucket" {
# ...
}
resource "aws_iam_user" "deploy_user" {
# ...
}
Related
I don't know if it is possible but I would like to take outputs from different modules as needed:
locals {
node_proyect =
[
["node_1", "project_A"],
["node_2", "project_B"],
...
["node_N", "project_N"],
]
}
Working modules:
module "node_1" {
...
}
[...]
module "node_N" {
...
}
trying to do:
module "outputs_sample" {
for_each = {for i,v in local.node_proyect: i=>v}
...
node_name = module.node_[each.value[0]].node_name
proyect_name = each.value[1]
...
}
What I want:
node_name --> module.node_node_1.node_name --> "string with the name with which the node has been created"
project_name --> "project_A"
Next for_each:
node_name --> module.node_node_2.node_name --> "string with the name with which the node has been created"
project_name --> "project_B"
But I get:
Error: Reference to undeclared module
│
│ on ....tf line ..., in module "outputs_sample":
│ 1073: node_name = module.node_[each.value[0]].node_name
│
│ No module call named "module.node_" is declared in module.....
The output of the modules exists and works perfectly, so if I do: module.node_node_1.node_name works.
I don't know how to make Terraform to interpret it like this, instead of literally as the error says: module.node_[each.value[0]].node_name
Given that you stated the modules are declared in the same config directory despite being absent from the config provided in the question, we can first fix the type and structure of the local variable:
locals {
node_proyect = {
"1" = "proyect_A",
"2" = "proyect_B"
}
}
Now we can use this for module iteration and fix the value accessors and string interpolation, and remove the unnecessary list constructor also causing issues:
module "outputs_sample" {
source = "./module"
for_each = local.node_proyect
...
node_name = "module.node_${each.key}.node_name"
proyect_name = each.value
...
}
But this will still causes issues due to a violation of the Terraform DSL. You need to restructure your module declarations (still absent from question so need to be hypothetical here):
module "node" {
...
for_each = local.node_proyect
...
}
and then the outputs can be accessed normally from the resulting map of objects:
module "outputs_sample" {
source = "./module"
for_each = local.node_proyect
...
node_name = module.node[each.key].node_name
proyect_name = each.value
...
}
After all of these fixes to your config you will achieve your goal.
Terraform Version
Terraform v1.1.2
on windows_amd64
Terraform Configuration Files
child_module1.tf(C1):
# Create Resource Group
resource "aws_resourcegroups_group" "resourcegroups_group" {
name = "test"
resource_query {
query = <<JSON
{
"ResourceTypeFilters": [
"AWS::EC2::Instance"
],
"TagFilters": [
{
"Key": "project",
"Values": ["${var.ProjectName}"]
}
]
}
JSON
}
}
child_module1_variables.tf:
########
variable "ProjectName" {
type = string
description = "This name would be prefixed with the cluster names."
}
Now call this child module in another child module**(C2)**:
child_module2.tf:
module "prepare_aws_cloud" {
source = "./modules/aws/prepare_cloud_copy"
ProjectName = "${var.test.ProjectName}"
}
child_module2_variables.tf:
variable "test" {
type = object({
ProjectName = string
})
}
Now I create a root module(R1)** which calls the child_module2.tf:**
terraform {
backend "local" {
}
}
module "test_deploy" {
source = "D:\\REPO\\installer_v2.2.22.1\\installer\\aws"
test = {
#ProjectName = ""
}
}
So the dependency is as follows:
R1 calls >> C2 calls >> C1
ERROR
PS D:\tkgTest> terraform apply -input=true
╷
│ Error: Invalid value for module argument
│
│ on testing.tf line 21, in module "test_deploy":
│ 21: test= {
│ 22: #ProjectName = ""
│ 23: }
│
│ The given value is not suitable for child module variable "test" defined at .terraform\modules\test_deploy\variables.tf:108,1-15: attribute "ProjectName" is required.
Expected Behavior
I would have expected that the user input would be taken interactively by the console as I am passing the -input=true flag but it doesn't seem to work.
The interactive prompts for input variables are intended only to help with getting started with Terraform (e.g. following a simple tutorial) and so they are limited in the scope of what they support. The typical way to set root module input variables for routine use is to either create a .tfvars file and pass it to Terraform with -var-file or to set a variable directly using -var.
Note also that only root module input variables can be set directly as part of the planning options. Any child module variables are defined exclusively by the expressions written in the module block, and so if you want to be able to set a child module's input variable on a per-run basis you'll need to also declare it as a root module variable and then pass it through to the child module.
For example, in the root module:
variable "test" {
type = object({
ProjectName = string
})
}
module "test_deploy" {
source = "./installer/aws"
test = var.test
}
You can then create an example.tfvars file with the following to set a value for the variable:
test = {
ProjectName = "example"
}
Specify that file when you run Terraform:
terraform apply -var-file=example.tfvars
If you will always set the same values then you can avoid the need for the extra option by naming your file example.auto.tfvars and placing it in the same directory where you will run Terraform. Terraform will load .auto.tfvars files automatically without any explicit -var-file option.
So here's my setup:
main.tf:
module "foo" {
source = "../../../foo-module"
name = "bar"
foo_nets = "${var.foo_nets}"
}
foo-module/terraform.tfvars:
foo_nets = ["1", "2", "3"]
➜ terraform plan -var-file=../../foo-module/terraform.tfvars -target=module.foo
Error: module 'foo': unknown variable referenced: 'foo_nets'; define it with a 'variable' block
Your foo-module/terraform.tfvars doesn't define a variable named foo_nets, it only defines a default value for a variable named foo_nets if that variable happens to exist. You still have to define the variable somewhere in the terraform files in the foo-module folder like this:
variable "foo_nets" {
}
please help to understand how I can import variables files from a different location?
I have tried to do this from the module system, but it's not working for me.
My structure :
/
/variables.tf
/my_ec2/main.tf
/my_ec2/variables.tf
How I can import variables from root folder? Need to specify it somehow on main.tf
My /my_ec2/main.tf
module "global_vars" {
source = "../../../"
}
provider "aws" {
region = "module.global_vars.region_aws"
}
my /variables.tf
variable "region_aws" {
default = "eu-central-1"
}
How can I do this?
P.S. Did the same with "${var.region_aws}", but same result
Error: Reference to undeclared input variable
on ../my_ec2/main.tf line 10, in resource "aws_instance" "server":
10: region = "${var.region_aws}"
An input variable with the name "aws_instance" has not been declared. This
variable can be declared with a variable "environment" {} block.
Maybe use :
"${module.global_vars.region_aws}"
Instead of
"module.global_vars.region_aws"
Terraform Version
Terraform v0.11.11
+ provider.azurerm v1.21.0
Terraform Configuration Files
I have left many required fields for brevity (all other config worked before I added the connection strings).
# modules/function/main.tf
variable "conn-value" {}
locals {
conn = "${map("name", "mydb", "value", "${var.conn-value}", "type", "SQLAzure")}"
}
resource "azurerm_function_app" "functions" {
connection_string = "${list(local.conn)}"
# ...
}
# modules/db/main.tf
# ... other variables declared
resource "azurerm_sql_server" "server" {
# ...
}
output "connection-string" {
value = "Server=tcp:${azurerm_sql_server.server.fully_qualified_domain_name},1433;Initial Catalog=${var.catalog};Persist Security Info=False;User ID=${var.login};Password=${var.login-password};MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=200;"
}
# main.tf
module "my_db" {
source = "modules/db"
}
module "my_app" {
source = "modules/function"
conn-value = "${module.my_db.connection-string}"
# ...
}
Expected Behavior on terraform plan
The module.my_db.connection-string output resolves to a string when passed to the my_app conn-value variable and is able to be used in the map/list passed to the azurerm_function_app.functions.connection_string variable.
Actual Behavior on terraform plan
I get this error:
module.my_app.azurerm_function_app.functions: connection_string: should be a list
If I replace "${var.conn-value}" in the modules/function/main.tf locals with just a string, it works.
Update
In response to to this comment, I updated the script above with the connection string construction.
I finally found the GitHub issue that references the problem I am having (I found the issue through this gist comment). This describes the problem exactly:
Assigning values to nested blocks is not supported, but appears to work in certain cases due to a number of coincidences...
This limitation is in <= v0.11, but is apparently fixed in v0.12 with the dynamic block.