Let's list the data we need to pass to the module:
That should be sufficient for now. Update the module inside template.tf
to look as follows:
module "mighty_trousers" { source = "./modules/application" vpc_id = "${aws_vpc.my_vpc.id}" subnet_id = "${aws_subnet.public.id}" name = "MightyTrousers" }
Passing data like this is not enough, though. We need to define variables inside the module template. The thing is, our tempate.tf
is a module itself, a special module named root module. That's what you saw on the last graph we drew--resources were coming from the root module. So, we were actually already using modules all this time, and every module, including the root module, can be configured with variables.
We did not look much into Terraform variables in general till now, and most of the content on this topic is explained in Chapter 4, Storing and Supplying Configuration. For now, let's get a short introduction to them.
Variables are defined with the variable
keyboard, followed by the variable name and optional default value inside curly braces:
variable number_of_servers { default = 1 }
There are many ways to define variables, and there are multiple types of variables, but let's save our in-depth exploration for the next chapter. For now, let's add the following lines to the top of ./modules/application/application.tf
file:
variable "vpc_id" {} variable "subnet_id" {} variable "name" {}
To use the variable, you need to reference it via a special var
keyword, as follows: ${var.my_variable}
. Replace all the resource references with variables:
resource "aws_security_group" "allow_http" { name = "${var.name} allow_http" description = "Allow HTTP traffic" vpc_id = "${var.vpc_id}" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } resource "aws_instance" "app-server" { ami = "ami-9bf712f4" instance_type = "t2.micro" subnet_id = "${var.subnet_id}" vpc_security_group_ids = ["${aws_security_group.allow_http.id}"] tags { Name = "${var.name}" } }
Now you should be able to run again via the terraform get
command:
$> terraform get
Get: file:///home/kshirinkin/work/packt-terraform-rewrites/modules/application
This created a symlink to your module inside the .terraform
directory:
$> ls -la .terraform/modules
total 12
drwxr-xr-x. 2 kshirinkin kshirinkin 4096 Jan 3 16:08 .
drwxr-xr-x. 3 kshirinkin kshirinkin 4096 Jan 3 16:08 ..
lrwxrwxrwx. 1 kshirinkin kshirinkin 66 Jan 3 16:08 8a6e0ac9202efe2b1f0a69ae2d5138bb -> /home/kshirinkin/work/packt-terraform-rewrites/modules/application
You should be able to run the terraform apply
command now. Let's add a second module, just to verify that we are still doing things right:
module "crazy_foods" { source = "./modules/application" vpc_id = "${aws_vpc.my_vpc.id}" subnet_id = "${aws_subnet.public.id}" name = "CrazyFoods" }
Run the terraform get
and terraform plan
commands, to check whether Terraform will do everything as expected and pay attention to this part of the output:
+ module.mighty_trousers.aws_security_group.allow_http description: "Allow HTTP traffic" name: "MightyTrousers allow_http"
Note how name of the resource was built; it includes the module name and a module
keyword. It doesn't mean that you can reference this module by this name though. Try to do it as follows:
module "crazy_foods" { source = "./modules/application" vpc_id = "${aws_vpc.my_vpc.id}" subnet_id = "${aws_subnet.public.id}" name = "CrazyFoods ${module.mighty_trousers.aws_security_group.allow_http.id}" }
You will get an error saying * module.crazy_foods: missing dependency: module.mighty_trousers.output.aws_security_group.allow_http.id
. You cannot simply reference resources inside module from outside the module. You have to use outputs.