This topic describes the usage of variables and outputs of Terraform modules.
Overview
Variables and outputs are the core components of the Terraform modules. They make modules reusable, flexible, and composable. Module variables allow you to customize the behavior of a module without modifying its source code. Output values enable the module to pass key information to its caller or other dependent modules. This parameterization and information transfer mechanism is the basis for building maintainable, extensible, and composable infrastructure code.
Variables provide flexibility for modules, allowing different configuration values to be used in different environments (such as development, test, and production environments), while maintaining the consistency of the infrastructure code. Terraform ensures the correctness and security of input data by using the variable type system and validation rules. Output values create communication channels between modules, allowing resource attributes (such as resource identifiers, connection strings, or network addresses) of one module to become input to another module, thereby establishing a dependency graph of the overall infrastructure.
Proper use of module variables and outputs is critical to creating standardized components that can be reused across regions and environments. For example, with parameterized network modules, security group modules, and compute resource modules, organizations can ensure that all teams consistently deploy infrastructure that meets enterprise standards. This topic describes the declarations, usage, and best practices of module variables and outputs, and uses examples to show how to apply these concepts in Alibaba Cloud infrastructure.
Module variables
In the examples so far, we have used hard-coded resource attributes. For example, in the main.tf file of the server module, the network name is hard-coded. If you use the same server module multiple times, you receive a name collision error when you run terraform plan or terraform apply.
In this case, we need to be able to flexibly configure different attribute values in different environments while maintaining standardization in other aspects. By using variables, we can customize aspects of the module without modifying the source code.
Importance of variables
Variables are especially useful when dealing with different environments, such as development, production, and staging environments. The best practice is to use smaller machine types in staging environments and larger machine types in production environments.
Use input variables to parameterize modules
Step 1: Replace hard-coded values
Replace hard-coded values in a module with variables. For example, to parameterize the network name:
# Original code
resource "alicloud_vpc" "main" {
vpc_name = "my-vpc" # Hard-coded value
}# Parameterized code
resource "alicloud_vpc" "main" {
vpc_name=var.vpc_name# Use variables
}Step 2: Declare variables
Declare the parameterized attributes in the variables.tf file. Make sure that the declared variables are placed in the same directory as the defined resources:
# variables.tf
variable "vpc_name" {
description = "Name of the VPC"
type = string
}Step 3: Pass a value when you call a module
When calling a module, pass the variable value:
module "vpc_dev" {
source = "./modules/vpc"
vpc_name = "dev-vpc"
}
module "vpc_prod" {
source = "./modules/vpc"
vpc_name = "prod-vpc"
}Note: Unlike the root configuration, you cannot pass values to module variables when Terraform is running.
Module output values
To pass a resource parameter from one module to another, you must configure the parameter as an output value.
Why do I need an output value?
To pass resource attributes between modules
To expose necessary resource information
To support dependencies between modules
Define and use output values
Define output values in the source module
# modules/vpc/outputs.tf
output "vpc_id" {
description = "ID of the created VPC"
value = alicloud_vpc.main.id
}
output "vswitch_ids" {
description = "IDs of the created vswitches"
value = alicloud_vswitch.vswitches[*].id
}Use output values in the destination module
# modules/ecs/main.tf
module "vpc" {
source = "../vpc"
}
resource "alicloud_instance" "example" {
vswitch_id = module.vpc.vswitch_ids[0]
}Example
Communication between the network module and the server module
# modules/network/main.tf
resource "alicloud_vpc" "main" {
vpc_name = var.vpc_name
cidr_block = var.vpc_cidr
}
# modules/network/outputs.tf
output "vpc_id" {
value = alicloud_vpc.main.id
}
# modules/ecs/variables.tf
variable "vpc_id" {
description = "ID of the VPC"
type = string
}
# root main.tf
module "network" {
source = "./modules/network"
vpc_name = "example-vpc"
vpc_cidr = "172.16.0.0/16"
}
module "ecs" {
source = "./modules/ecs"
vpc_id = module.network.vpc_id
}Multi-environment configuration example
# environments/dev.tfvars
environment = "dev"
instance_type = "ecs.g6.small"
instance_count = 1
# environments/prod.tfvars
environment = "prod"
instance_type = "ecs.g6.2xlarge"
instance_count = 3
# Use different environment configurations
terraform apply -var-file="environments/dev.tfvars"Best practices
Variable design
Define variables for all available configuration items
Provide reasonable default values
Use variable validation to ensure valid inputs
Add clear variable descriptions
Output design
Expose only the necessary output values
Provide descriptions for output values
Consider the requirements of downstream modules
Environment management
Separate environments by using workspaces
Create special variable files for different environments
Use conditional expressions to handle environment differences
Usage notes
You cannot input module variables when Terraform is running
You need to run
terraform initafter each module modificationTake note of type matching of variable values