Terraform HCL Syntax, Variables, Data-types, Expressions & Configuration files
TerraWeek Day 2/7
Task 1: Familiarize yourself with the HCL syntax used in Terraform
Learn about HCL blocks, parameters, and arguments
HCL (HashiCorp Configuration Language) is the language used in Terraform for defining infrastructure resources in code. When writing Terraform code in HCL, you typically define resources using a block declaration, specify parameters within the block, and then provide arguments to those parameters.
Block is a container that groups a set of parameters to define a specific object or resource. Blocks are a fundamental aspect of HCL and are commonly used when working with infrastructure provisioning tools such as Terraform.
The block syntax in HCL is defined by curly braces {}
and consists of a block type and one or more attributes or parameters. The block type defines the kind of object or resource being created, while the attributes specify the properties or attributes of the object being created.
block_Object "block_type" "label" {
parameter_1 = argument_1
parameter_2 = argument_2
parameter_3 = argument_3
}
For example, below is a simple HCL block that creates an AWS S3 bucket resource:
resource "aws_s3_bucket" "example" {
bucket = "my-example-bucket"
acl = "private"
}
In this example, the block type is aws_s3_bucket
, which defines an AWS S3 bucket resource. The block contains two parameters, bucket
and acl
, which are defined using the =
delimiter. The aws_s3_bucket.example
syntax is used to reference the bucket resource instance elsewhere in the Terraform configuration.
Here is an example block, parameter, and argument using the aws_instance
resource:
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
tags = {
Name = "example-instance"
}
}
In this example, aws_instance
is the block type, example
is the name of the resource instance, ami
and instance_type
are parameters specific to the aws_instance
resource, and ami-0c55b159cbfafe1f0
and t2.micro
are the arguments for those parameters.
You can think of a block as a container for parameters that define a specific resource type, such as aws_instance
, aws_subnet
, or aws_security_group
. Each block also has a unique identifier that is used to reference the resource throughout your Terraform code. In the example above, the identifier is "example" and the full reference to the resource is aws_instance.example
.
Parameters are properties specific to the block type, such as ami
and instance_type
for the aws_instance
block. These are used to define the resource's behavior and can accept one or more arguments.
Lastly, arguments are the values assigned to the parameters, such as ami-0c55b159cbfafe1f0
or t2.micro
in the example above. These are the values that will be set for the resource when it is created or updated in your cloud provider.
Explore the different types of resources and data sources available in Terraform
Sure, here are the different types of resources and data sources available in Terraform:
Resources are the fundamental building blocks of Terraform configurations. They represent the infrastructure objects that need to be created, updated, or deleted. Some of the commonly used resources in Terraform are:
aws_instance:
resource "aws_instance" "example" {
ami = "ami-0c94855ba95c71c99"
instance_type = "t2.micro"
subnet_id = aws_subnet.example.id
# Other configuration options like security groups, tags, etc.
}
aws_security_group:
resource "aws_security_group" "example" {
name = "example-security-group"
description = "Example security group"
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# Other ingress and egress rules
}
aws_s3_bucket:
resource "aws_s3_bucket" "example" {
bucket = "example-bucket"
acl = "private"
# Other bucket configuration options
}
aws_rds_instance:
resource "aws_rds_instance" "example" {
engine = "mysql"
instance_class = "db.t2.micro"
allocated_storage = 10
# Other database configuration options
}
aws_lambda_function:
resource "aws_lambda_function" "example" {
function_name = "example-function"
runtime = "python3.8"
handler = "lambda_function.lambda_handler"
# Other function configuration options like code, environment variables, etc.
}
google_compute_instance:
resource "google_compute_instance" "example" {
name = "example-instance"
machine_type = "n1-standard-1"
zone = "us-central1-a"
# Other instance configuration options like image, network, etc.
}
google_storage_bucket:
resource "google_storage_bucket" "example" {
name = "example-bucket"
location = "us-central1"
# Other bucket configuration options
}
azurerm_virtual_machine:
resource "azurerm_virtual_machine" "example" {
name = "example-vm"
location = "eastus"
resource_group_name = azurerm_resource_group.example.name
vm_size = "Standard_DS1_v2"
# Other virtual machine configuration options like image, networking, etc.
}
Task 2: Understand variables, data types, and expressions in HCL
variables.tf syntax:
variable "variable_name" {
description = "Write something about variable use or related to it"
default = "variable_value"
type = "variable_type"
}
- Defining Variables in HCL, a few examples:
variables.tf
variable "region" {
description = "The AWS region to deploy resources"
default = "us-west-2"
}
variable "instance_type" {
description = "The type of EC2 instance"
type = string
default = "t2.micro"
}
variable "enable_backup" {
description = "Enable backup for the resources"
type = bool
default = true
}
- Using Variables in HCL Expressions:
main.tf:
resource "aws_instance" "example" {
ami = "ami-0c94855ba95c71c99"
instance_type = var.instance_type
subnet_id = aws_subnet.example.id
tags = {
Name = "Example Instance"
Environment = "Development"
}
}
- Performing Expressions and Functions in HCL:
main.tf:
resource "aws_ebs_volume" "example" {
availability_zone = "us-west-2a"
size = 100
encrypted = true
# Constructing the volume name using an expression
tags = {
Name = "Volume ${var.region}-${aws_instance.example.id}"
}
# Using a function to generate a random ID for the volume
snapshot_id = data.aws_ebs_snapshot.example.id
volume_id = random_id.volume_id.hex
}
- Working with Data Types in HCL:
In HCL (HashiCorp Configuration Language) used in Terraform, the following data types are commonly used:
string: Represents a sequence of characters. Strings are enclosed in double quotes (").
variable "name" {
type = string
default = "John Doe"
}
resource "aws_instance" "example" {
ami = "ami-0c94855ba95c71c99"
instance_type = "t2.micro"
tags = {
Name = var.name
}
}
number: Represents numerical values, including both integers and floating-point numbers.
variable "count" {
type = number
default = 5
}
resource "aws_instance" "example" {
count = var.count
ami = "ami-0c94855ba95c71c99"
instance_type = "t2.micro"
}
**bool**: Represents boolean values, which can be either true or false.
variable "enable_logging" {
type = bool
default = true
}
resource "aws_s3_bucket" "example" {
bucket = "my-bucket"
acl = var.enable_logging ? "private" : "public-read"
}
list: Represents an ordered collection of values of the same or different data types. Lists are defined using square brackets ([]) and can contain any valid Terraform data type.
variable "ports" {
type = list(number)
default = [80, 443, 8080]
}
resource "aws_security_group" "example" {
name = "example-security-group"
description = "Example security group"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
dynamic "ingress" {
for_each = var.ports
content {
from_port = ingress.value
to_port = ingress.value
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
}
map: Represents an unordered collection of key-value pairs. Maps are defined using curly braces ({}) and can contain any valid Terraform data types.
variable "tags" {
type = map(string)
default = {
Environment = "Development"
Owner = "John Doe"
}
}
resource "aws_instance" "example" {
ami = "ami-0c94855ba95c71c99"
instance_type = "t2.micro"
tags = var.tags
}
object: Represents a complex data structure that combines multiple attributes and values into a single entity. Objects are similar to maps but have a defined structure with specific attribute names and data types.
variable "server" {
type = object({
name = string
cpu = number
memory = number
disk = number
location = string
})
default = {
name = "web-server"
cpu = 2
memory = 8
disk = 100
location = "us-west-2"
}
}
resource "aws_instance" "example" {
ami = "ami-0c94855ba95c71c99"
instance_type = "t2.micro"
cpu_core = var.server.cpu
# Other configuration options
}
tuple: Represents an ordered collection of values of different data types. Tuples are defined using parentheses (()) and can contain any valid Terraform data types.
variable "subnet_ids" {
type = tuple(string)
default = ("subnet-abc123", "subnet-def456", "subnet-ghi789")
}
resource "aws_instance" "example" {
ami = "ami-0c94855ba95c71c99"
instance_type = "t2.micro"
subnet_id = element(var.subnet_ids, count.index)
count = length(var.subnet_ids)
}
set: Represents an unordered collection of unique values. Sets are defined using curly braces ({}) and can contain any valid Terraform data types.
variable "allowed_regions" {
type = set(string)
default = ["us-east-1", "us-west-2"]
}
resource "aws_security_group" "example" {
name = "example-security-group"
description = "Example security group"
# Allow inbound traffic from the allowed regions
dynamic "ingress" {
for_each = var.allowed_regions
content {
from_port = 0
to_port = 65535
protocol = "-1"
cidr_blocks = [cidrsubnet("10.0.0.0/16", 8, 0)] # Example CIDR block
}
}
}
any: Represents a dynamic data type that can hold any value, allowing flexibility in handling different data types.
variable "dynamic_value" {
type = any
default = {
name = "John Doe"
age = 30
email = "johndoe@example.com"
}
}
resource "aws_instance" "example" {
ami = "ami-0c94855ba95c71c99"
instance_type = "t2.micro"
tags = var.dynamic_value
}
These data types provide the foundation for defining variables, expressions, and complex data structures in Terraform configurations using HCL. Understanding and utilizing these data types effectively can greatly enhance the flexibility and versatility of your infrastructure provisioning code.
main.tf:
variable "ports" {
description = "A list of ports to open"
type = list(number)
default = [80, 443, 8080]
}
resource "aws_security_group" "example" {
name = "example-security-group"
description = "Example security group"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# Using a for-each loop to dynamically allow ports
dynamic "ingress" {
for_each = var.ports
content {
from_port = ingress.value
to_port = ingress.value
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
}
In the examples above, we defined variables with different data types, used them in resource blocks, performed expressions and functions, and worked with lists. These snippets highlight the flexibility and power of HCL when it comes to defining infrastructure configurations with dynamic values and expressions.
Create variables.tf file and define a variable
Use the variable in a main.tf file to create a "local_file" resource
terraform init
terraform plan
terraform apply
Task 3: Practice writing Terraform configurations using HCL syntax
Add required_providers to your configuration, such as Docker or AWS
Test your configuration using the Terraform CLI and make any necessary adjustments
Happy learning :)
#TrainWithShubham #TerraWeek Challenge
I hope you learned something today with me!
Stay tuned for my next blog on "Day3 of Terraweek Challenge". I will keep sharing my learnings and knowledge here with you.
Let's learn together! I appreciate any comments or suggestions you may have to improve my learning and blog content.
Thank you,
Chaitannyaa Gaikwad