Eventually, I got to the modules in Terraform.
Namely, I had to figure out how to transfer the values of the variables between the two modules.
So in this post, the most basic and simple examples of working with modules and their values && outputs.
See more in the documentation — Modules.
The Root module
First, let’s create a root module, which simply creates a local file, and where later, we will add the modules.
Create a testing directory:
$ mkdir modules_example
$ cd modules_example/
In this directory add a file main.tf
with the resource
of the local_file
type that will create a file.txt with the "file content" text:
resource "local_file" "file" {
content = "file content"
filename = "file.txt"
}
Run terraform init
to pull up the necessary modules of Terraform itself:
$ terraform init
Initializing the backend…
Initializing provider plugins…
- Finding latest version of hashicorp/local…
- Installing hashicorp/local v2.2.3…
- Installed hashicorp/local v2.2.3 (signed by HashiCorp)
…
Terraform has been successfully initialized!
Then, run terraform plan
to check whether it will work at all, and what exactly it will do:
$ terraform plan
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
local_file.file will be created
+ resource “local_file” “file” {
+ content = “file content”
+ directory_permission = “0777”
+ file_permission = “0777”
+ filename = “file.txt”
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
If it looks OK, run the terraform apply
:
$ terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
local_file.file will be created
+ resource “local_file” “file” {
+ content = “file content”
+ directory_permission = “0777”
+ file_permission = “0777”
+ filename = “file.txt”
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only ‘yes’ will be accepted to approve.
Enter a value: yes
local_file.file: Creating…
local_file.file: Creation complete after 0s [id=87758871f598e1a3b4679953589ae2f57a0bb43c]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
And check the contents of the directory where we created the main.tf
file and launched the commands:
$ ls -l
total 12
-rwxr-xr-x 1 setevoy setevoy 12 Nov 28 13:14 file.txt
-rw-r — r — 1 setevoy setevoy 90 Nov 28 13:09 main.tf
-rw-r — r — 1 setevoy setevoy 854 Nov 28 13:14 terraform.tfstate
And the file.txt
content:
$ cat file.txt
file content
It works! let’s go further.
Terraform Modules
Next, let’s go to the modules.
Create two directories for two modules:
$ mkdir -p modules/file_1
$ mkdir -p modules/file_2
In each of them, create their own main.tf
files - the modules/file_1/main.tf
and modules/file_2/main.tf
.
In the modules/file_1/main.tf
use the same local_file
resource to create a file_1.txt file :
resource "local_file" "file_1" {
content = "file_1 content"
filename = "file_1.txt"
}
Similarly in the modules/file_2/main.tf
for the file_2.txt:
resource "local_file" "file_2" {
content = "file_2 content"
filename = "file_2.txt"
}
Update the root module, that is, modules_example/main.tf
- delete the resource "local_file
", and instead of it describe the two modules with the paths to the directories of both modules:
module "file_1" {
source = "./modules/file_1"
}
module "file_2" {
source = "./modules/file_2"
}
Run the init again so that Terraform creates its modules structure:
$ terraform init
Initializing modules…
- file_1 in modules/file_1
- file_2 in modules/file_2
Initializing the backend…
Initializing provider plugins…
- Reusing previous version of hashicorp/local from the dependency lock file
- Using previously-installed hashicorp/local v2.2.3
Terraform has been successfully initialized!
Check the .terraform/modules/
:
$ cat .terraform/modules/modules.json | jq
{
“Modules”: [
{
“Key”: “”,
“Source”: “”,
“Dir”: “.”
},
{
“Key”: “file_1”,
“Source”: “./modules/file_1”,
“Dir”: “modules/file_1”
},
{
“Key”: “file_2”,
“Source”: “./modules/file_2”,
“Dir”: “modules/file_2”
}
]
}
Run plan
:
$ terraform plan
local_file.file: Refreshing state… [id=87758871f598e1a3b4679953589ae2f57a0bb43c]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
- destroy
Terraform will perform the following actions:
local_file.file will be destroyed
(because local_file.file is not in configuration)
- resource “local_file” “file” {
- content = “file content” -> null
- directory_permission = “0777” -> null
- file_permission = “0777” -> null
- filename = “file.txt” -> null
- id = “87758871f598e1a3b4679953589ae2f57a0bb43c” -> null
}
module.file_1.local_file.file_1 will be created
+ resource “local_file” “file_1” {
+ content = “file_1 content”
+ directory_permission = “0777”
+ file_permission = “0777”
+ filename = “file_1.txt”
+ id = (known after apply)
}
module.file_2.local_file.file_2 will be created
+ resource “local_file” “file_2” {
+ content = “file_2 content”
+ directory_permission = “0777”
+ file_permission = “0777”
+ filename = “file_2.txt”
+ id = (known after apply)
}
Plan: 2 to add, 0 to change, 1 to destroy.
And now we can perform the apply.
In order not to enter “yes” every time, we can use the -auto-approve
argument:
$ terraform apply -auto-approve
local_file.file: Refreshing state… [id=87758871f598e1a3b4679953589ae2f57a0bb43c]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
- destroy
Terraform will perform the following actions:
local_file.file will be destroyed
(because local_file.file is not in configuration)
- resource “local_file” “file” {
- content = “file content” -> null
- directory_permission = “0777” -> null
- file_permission = “0777” -> null
- filename = “file.txt” -> null
- id = “87758871f598e1a3b4679953589ae2f57a0bb43c” -> null
}
module.file_1.local_file.file_1 will be created
+ resource “local_file” “file_1” {
+ content = “file_1 content”
+ directory_permission = “0777”
+ file_permission = “0777”
+ filename = “file_1.txt”
+ id = (known after apply)
}
module.file_2.local_file.file_2 will be created
+ resource “local_file” “file_2” {
+ content = “file_2 content”
+ directory_permission = “0777”
+ file_permission = “0777”
+ filename = “file_2.txt”
+ id = (known after apply)
}
Plan: 2 to add, 0 to change, 1 to destroy.
local_file.file: Destroying… [id=87758871f598e1a3b4679953589ae2f57a0bb43c]
module.file_2.local_file.file_2: Creating…
module.file_1.local_file.file_1: Creating…
local_file.file: Destruction complete after 0s
module.file_2.local_file.file_2: Creation complete after 0s [id=7225b36c22072cd558c23529d0d992c29cb873be]
module.file_1.local_file.file_1: Creation complete after 0s [id=82888a6ec6b05fc219759bd241ca5f0d6cba0e23]
Apply complete! Resources: 2 added, 0 changed, 1 destroyed.
Check whether the files have appeared:
$ ll
total 24
-rwxr-xr-x 1 setevoy setevoy 14 Nov 28 13:21 file_1.txt
-rwxr-xr-x 1 setevoy setevoy 14 Nov 28 13:21 file_2.txt
-rw-r — r — 1 setevoy setevoy 104 Nov 28 13:18 main.tf
drwxr-xr-x 4 setevoy setevoy 4096 Nov 28 13:15 modules
And their content:
$ cat file_1.txt
file_1 content
cat file_2.txt
file_2 content
Works? Move on.
Variables in Terraform modules
There is nothing special here - everything is the same as with common Terraform variables. See the documentation - Input Variables.
In the modules_example/modules/file_1/main.tf
declare a variable user_name
:
variable "user_name" {
type = string
}
resource "local_file" "file_1" {
content = "file_1 content from ${var.user_name}"
filename = "file_1.txt"
}
The same in the second module:
variable "user_name" {
type = string
}
resource "local_file" "file_2" {
content = "file_2 content from ${var.user_name}"
filename = "file_2.txt"
}
Update the root module — pass the values for the user_name
variable for both modules:
module "file_1" {
user_name = "user1"
source = "./modules/file_1"
}
module "file_2" {
user_name = "user2"
source = "./modules/file_2"
}
Apply the changes:
$ terraform apply -auto-approve
module.file_1.local_file.file_1: Refreshing state… [id=82888a6ec6b05fc219759bd241ca5f0d6cba0e23]
module.file_2.local_file.file_2: Refreshing state… [id=7225b36c22072cd558c23529d0d992c29cb873be]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
module.file_1.local_file.file_1 must be replaced
-/+ resource “local_file” “file_1” {
~ content = “file_1 content” -> “file_1 content from user1” # forces replacement
~ id = “82888a6ec6b05fc219759bd241ca5f0d6cba0e23” -> (known after apply)
(3 unchanged attributes hidden)
}
module.file_2.local_file.file_2 must be replaced
-/+ resource “local_file” “file_2” {
~ content = “file_2 content” -> “file_2 content from user2” # forces replacement
~ id = “7225b36c22072cd558c23529d0d992c29cb873be” -> (known after apply)
(3 unchanged attributes hidden)
}
…
Apply complete! Resources: 2 added, 0 changed, 2 destroyed.
And the result check:
$ cat file_1.txt
file_1 content from user1
Modules and Output values of the variables
How can we pass the value of the variable from the child module to the root module? Use outputs
. See Output Values documentation.
Update the first module - add the outputwith the name "file_content":
variable "user_name" {
type = string
}
resource "local_file" "file_1" {
content = "file_1 content from ${var.user_name}"
filename = "file_1.txt"
}
output "file_content" {
value = file("file_1.txt")
}
The same in the second:
variable "user_name" {
type = string
}
resource "local_file" "file_2" {
content = "file_2 content from ${var.user_name}"
filename = "file_2.txt"
}
output "file_content" {
value = file("file_2.txt")
}
And then in the root module, use the values obtained using with the module.<MODULE_NAME>.<OUTPUD_NAME>
to create a file concat_file.txt :
module "file_1" {
user_name = "user1"
source = "./modules/file_1"
}
module "file_2" {
user_name = "user2"
source = "./modules/file_2"
}
resource "local_file" "concat_file" {
content = "${module.file_1.file_content}\n${module.file_2.file_content}\n"
filename = "concat_file.txt"
}
Run apply:
$ terraform apply -auto-approve
module.file_2.local_file.file_2: Refreshing state… [id=5771aa8b1046de4f4342d492faee20e3c289365d]
module.file_1.local_file.file_1: Refreshing state… [id=8a3552bfa2e46f311e7b718f8eed71b69bb8115f]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
local_file.concat_file will be created
+ resource “local_file” “concat_file” {
+ content = <<-EOT
file_1 content from user1
file_2 content from user2
EOT
+ directory_permission = “0777”
+ file_permission = “0777”
+ filename = “concat_file.txt”
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
local_file.concat_file: Creating…
local_file.concat_file: Creation complete after 0s [id=a4e1240fb9cdc96fffc1b0984392f6218422d194]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
Check the result:
$ cat concat_file.txt
file_1 content from user1
file_2 content from user2
Transfer variable values between modules
And finally, what we started with - how to transfer values from one module to another?
In the file main.tf
of the first module, add a variable with the name module_1_value
and a default value "module 1 value":
...
variable "module_1_value" {
type = string
default = "module 1 value"
}
...
In the same place, create a second variable module_2_value
, into which we will then pass the value from the second module:
...
variable "module_2_value" {
type = string
}
...
Add the output to return the value of the variable module_1_value
to the root module for further use in the second module:
...
output "module_1_value" {
value = var.module_1_value
}
...
The complete file now looks like this:
variable "user_name" {
type = string
}
variable "module_1_value" {
type = string
default = "module 1 value"
}
variable "module_2_value" {
type = string
}
resource "local_file" "file_1" {
content = "file_1 content from ${var.user_name} with value from file_2: ${var.module_2_value}"
filename = "file_1.txt"
}
output "file_content" {
value = file("file_1.txt")
}
output "module_1_value" {
value = var.module_1_value
}
Here in the resource local_file
we will use the value transferred from the second module.
Repeat the same for the module file_2
:
variable "user_name" {
type = string
}
variable "module_2_value" {
type = string
default = "module 2 value"
}
variable "module_1_value" {
type = string
}
resource "local_file" "file_2" {
content = "file_2 content from ${var.user_name} with value from file_1: ${var.module_1_value}"
filename = "file_2.txt"
}
output "file_content" {
value = file("file_2.txt")
}
output "module_2_value" {
value = var.module_2_value
}
Return to the root module and add the transfer of the variable module_2_value
to the first module, and the variable module_1_value
to the second module:
module "file_1" {
user_name = "user1"
module_2_value = module.file_2.module_2_value
source = "./modules/file_1"
}
module "file_2" {
user_name = "user2"
module_1_value = module.file_1.module_1_value
source = "./modules/file_2"
}
resource "local_file" "concat_file" {
content = "${module.file_1.file_content}\n${module.file_2.file_content}\n"
filename = "concat_file.txt"
}
Now in the file file_1.txt
we have to get the value from the module "file_2
", and vice versa.
Apply:
$ terraform apply -auto-approve
module.file_1.local_file.file_1: Refreshing state… [id=d74b0e0b3296ab02e7b4791942d983b175d071b4]
local_file.concat_file: Refreshing state… [id=e0e88293b53693a484db146b15bd2ab3d8f4d250]
module.file_2.local_file.file_2: Refreshing state… [id=12faf027dc96d886c6022b54c878324a21d3d112]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
local_file.concat_file must be replaced
-/+ resource “local_file” “concat_file” {
~ content = <<-EOT # forces replacement
- file_1 content from user1 with value from file_2: variable 2 value
- file_2 content from user2 with value from file_1: variable 1 value
+ file_1 content from user1 with value from file_2: module 2 value
+ file_2 content from user2 with value from file_1: module 1 value
EOT
~ id = “e0e88293b53693a484db146b15bd2ab3d8f4d250” -> (known after apply)
(3 unchanged attributes hidden)
}
Plan: 1 to add, 0 to change, 1 to destroy.
local_file.concat_file: Destroying… [id=e0e88293b53693a484db146b15bd2ab3d8f4d250]
local_file.concat_file: Destruction complete after 0s
local_file.concat_file: Creating…
local_file.concat_file: Creation complete after 0s [id=648854841581b273d26646fff776b33fdf060a19]
Apply complete! Resources: 1 added, 0 changed, 1 destroyed.
Check the result:
$ cat concat_file.txt
file_1 content from user1 with value from file_2: module 2 value
file_2 content from user2 with value from file_1: module 1 value
Done.
Originally published at RTFM: Linux, DevOps, and system administration.
Top comments (0)