This page contains a quick reference for writing Terraform configuration. For a conceptual introduction to Terraform and managing your infrastructure as code, read this blog post.
Disclaimer: for the most up-to-date and detailed information, check out the official Terraform documentation.
Jump to:
Configuration blocks
Terraform
Configuration block that allows you to specify various Terraform settings (such as required versions for Terraform and/or its providers).
terraform {
required_providers {
# e.g. include the AWS provider and require a specific major version
aws = {
source = "hashicorp/aws"
version = ">= 4.0.0, < 5.0.0"
}
}
required_version = ">= 1.2.3"
}
Provider
Versioned code which is used to communicate with an external infrastructure provider.
provider "provider_name" {
# provider-specific configuration
# e.g. what region should this provider create your resources in?
}
Resource
Smallest unit of infrastructure managed by a provider.
resource "resource_type" "resource_name" {
# resource-specific configuration
}
- The
resource_type
always starts with the prefix{provider}_
- You can reference a given resource in other parts of your Terraform configuration using the format
{resource_type}.{resource_name}
- There's a small set of meta-arguments which you can use for any resource:
count
: create multiple instances of the same resource specificationfor_each
: configure a set of resources based on a provider iterableprovider
: override the default provider configuration (e.g. changing the region to deploy a specific resource)depends_on
: specify hidden dependencies between resources that Terraform isn't able to inferlifecycle
: configuration around resource lifecycle management (e.g. create a new version of the resource before destroying the old one)
Variable
Allow end users to specify values (to be used in resource creation) when defining a module.
variable "variable_name" {
type = string
description = ""
# optional
default = ""
sensitive = true
validation {
condition = ...
error_message = ""
}
}
- Usually defined in a separate file,
variables.tf
, for readability - These values are constant, they cannot be changed during planning or execution
- You can reference a given variable as
var.{variable_name}
- Supported types:
- simple (
string
,bool
,number
) - collection (
map
,list
,set
) - structural (
tuple
,object
)
- simple (
- You can optionally add one or more validation conditions (e.g. to make sure a resource matches AWS resource naming requirements). This can be helpful for "failing fast" when you provide improper values.
- By default, variables are not treated as sensitive. You can specify
sensitive = true
in order to prevent Terraform from outputing these values to the console.
Output
Expose a specific attribute of a resource within a module.
output "output_name" {
description = ""
value = resource_type.resource_name.attribute
# optional
sensitive = true
precondition {
condition = ...
error_message = ""
}
}
- Usually defined in a separate file,
outputs.tf
, for readability - You can reference a module's output as
{module_name}.{output_name}
- You can optionally add one or more preconditions to check before returning an output value (e.g. to check that a certificate status is set to
ISSUED
). This can be used as a last line of defense to validate your assumptions before returning data about a given resource. - By default, variables are not treated as sensitive. You can specify
sensitive = true
in order to prevent Terraform from outputing these values to the console.
Module
A logical grouping of resources which can be configured and deployed together.
module "module_name" {
source = "../path/to/module"
# optional
version = ""
}
- Encourages reuse of configuration and provides consistency across your infrastructure stack
- There’s a small set of universal “meta-arguments” that you can use for any module:
count
: create multiple instances of the same module specificationfor_each
: configure a set of modules based on a provider iterableprovider
: override the default provider configuration (e.g. changing the region to deploy the resources in your module)depends_on
: specify hidden dependencies between your module and other resources that Terraform isn't able to infer
- Modules can be referenced from local filepaths or remote repositories (Terraform registry, Github, S3)
In addition to using existing modules available on the Terraform registry, you can also define your own. A module can be defined as a set of Terraform configuration files within a single directory.
.
├── LICENSE
├── README.md
├── main.tf
├── variables.tf
├── outputs.tf
Data
Retrieve information from an external infrastructure provider.
data "data_resource_type" "data_resource_name" {
# parameters used to retrieve information
}
Locals
Used to provide a succinct or readable name for a Terraform expression which may be referenced multiple times.
locals {
# e.g. define a set of required tags to add to all resources
required_tags = {
project = var.project_name,
environment = var.environment
}
tags = merge(var.resource_tags, local.required_tags)
# e.g. define a common suffix for use in multiple resource definitions
name_suffix = "${var.project_name}-${var.environment}"
}
- Can be referenced as
locals.{attribute}
- Dynamic expressions (e.g. using Terraform functions) are allowed, providing more flexibility than you have available in Terraform variables
- It's recommended you use locals sparringly, keeping in mind the trade-offs between readability and DRY principles
- In other words, don't make someone constantly scroll up to the
locals
block in order to figure out how a given resource is being configured
- In other words, don't make someone constantly scroll up to the
Dynamic expressions
Conditional statements
You can express conditional statements in Terraform, although I find the syntax a bit odd.
attribute = (conditional_expression ? value_if_true : value_if_false)
Interpret ?
as “then” and :
as “else” when reading.
The conditional_expression
itself is defined with common syntax:
Symbol | Meaning |
---|---|
!= |
not equal |
== |
equal |
Wildcards
You can use a splat *
expression to reference a given attribute over a list of resources.
resource "aws_instance" "app_servers" {
count = 5
...
}
output "private_addresses" {
value = aws_instance.app_servers[*].private_dns
}
Comprehensions
You can create lists and maps using syntax similar to list/dictionary comprehensions in Python.
The general syntax is [for item in iterable: value]
for a list and {for item in iterable: key => value}
for a map. You can also add conditional statements, such as in the example below.
locals {
tags = tomap({ foo = "123", bar = "456", empty = "" })
}
value = { for key, value in local.tags : key => value if value != "" }