Naming conventions
General conventions
Use
_
(underscore) instead of-
(dash) everywhere (in resource names, data source names, variable names, outputs, etc).Prefer to use lowercase letters and numbers (even though UTF-8 is supported).
Resource and data source arguments
Do not repeat resource type in resource name (not partially, nor completely):
`resource "aws_route_table" "public" {}`
`resource "aws_route_table" "public_route_table" {}`
`resource "aws_route_table" "public_aws_route_table" {}`
Resource name should be named
this
if there is no more descriptive and general name available, or if the resource module creates a single resource of this type (eg, in AWS VPC module there is a single resource of typeaws_nat_gateway
and multiple resources of typeaws_route_table
, soaws_nat_gateway
should be namedthis
andaws_route_table
should have more descriptive names - likeprivate
,public
,database
).Always use singular nouns for names.
Use
-
inside arguments values and in places where value will be exposed to a human (eg, inside DNS name of RDS instance).Include argument
count
/for_each
inside resource or data source block as the first argument at the top and separate by newline after it.Include argument
tags,
if supported by resource, as the last real argument, following bydepends_on
andlifecycle
, if necessary. All of these should be separated by a single empty line.When using conditions in an argument
count
/for_each
prefer boolean values instead of usinglength
or other expressions.
Code examples of resource
resource
Usage of count
/ for_each
count
/ for_each
resource "aws_route_table" "public" {
count = 2
vpc_id = "vpc-12345678"
# ... remaining arguments omitted
}
resource "aws_route_table" "private" {
for_each = toset(["one", "two"])
vpc_id = "vpc-12345678"
# ... remaining arguments omitted
}
resource "aws_route_table" "public" {
vpc_id = "vpc-12345678"
count = 2
# ... remaining arguments omitted
}
Placement of tags
tags
resource "aws_nat_gateway" "this" {
count = 2
allocation_id = "..."
subnet_id = "..."
tags = {
Name = "..."
}
depends_on = [aws_internet_gateway.this]
lifecycle {
create_before_destroy = true
}
}
resource "aws_nat_gateway" "this" {
count = 2
tags = "..."
depends_on = [aws_internet_gateway.this]
lifecycle {
create_before_destroy = true
}
allocation_id = "..."
subnet_id = "..."
}
Conditions in count
count
resource "aws_nat_gateway" "that" { # Best
count = var.create_public_subnets ? 1 : 0
}
resource "aws_nat_gateway" "this" { # Good
count = length(var.public_subnets) > 0 ? 1 : 0
}
Variables
Don't reinvent the wheel in resource modules: use
name
,description
, anddefault
value for variables as defined in the "Argument Reference" section for the resource you are working with.Support for validation in variables is rather limited (e.g. can't access other variables or do lookups if using a version before Terraform
1.9
). Plan accordingly because in many cases this feature is useless.Use the plural form in a variable name when type is
list(...)
ormap(...)
.Order keys in a variable block like this:
description
,type
,default
,validation
.Always include
description
on all variables even if you think it is obvious (you will need it in the future). Use the same wording as the upstream documentation when applicable.Prefer using simple types (
number
,string
,list(...)
,map(...)
,any
) over specific type likeobject()
unless you need to have strict constraints on each key.Use specific types like
map(map(string))
if all elements of the map have the same type (e.g.string
) or can be converted to it (e.g.number
type can be converted tostring
).Use type
any
to disable type validation starting from a certain depth or when multiple types should be supported.Value
{}
is sometimes a map but sometimes an object. Usetomap(...)
to make a map because there is no way to make an object.Avoid double negatives: use positive variable names to prevent confusion. For example, use
encryption_enabled
instead ofencryption_disabled
.For variables that should never be
null
, setnullable = false
. This ensures that passingnull
uses the default value instead ofnull
. Ifnull
is an acceptable value, you can omit nullable or set it totrue
.
Outputs
Make outputs consistent and understandable outside of its scope (when a user is using a module it should be obvious what type and attribute of the value it returns).
The name of output should describe the property it contains and be less free-form than you would normally want.
Good structure for the name of output looks like
{name}_{type}_{attribute}
, where:{name}
is a resource or data source name{name}
fordata "aws_subnet" "private"
isprivate
{name}
forresource "aws_vpc_endpoint_policy" "test"
istest
{type}
is a resource or data source type without a provider prefix{type}
fordata "aws_subnet" "private"
issubnet
{type}
forresource "aws_vpc_endpoint_policy" "test"
isvpc_endpoint_policy
{attribute}
is an attribute returned by the output
If the output is returning a value with interpolation functions and multiple resources,
{name}
and{type}
there should be as generic as possible (this
as prefix should be omitted). See example.If the returned value is a list it should have a plural name. See example.
Always include
description
for all outputs even if you think it is obvious.Avoid setting
sensitive
argument unless you fully control usage of this output in all places in all modules.Prefer
try()
(available since Terraform 0.13) overelement(concat(...))
(legacy approach for the version before 0.13)
Code examples of output
output
Return at most one ID of security group:
output "security_group_id" {
description = "The ID of the security group"
value = try(aws_security_group.this[0].id, aws_security_group.name_prefix[0].id, "")
}
When having multiple resources of the same type, this
should be omitted in the name of output:
output "this_security_group_id" {
description = "The ID of the security group"
value = element(concat(coalescelist(aws_security_group.this.*.id, aws_security_group.web.*.id), [""]), 0)
}
Use plural name if the returning value is a list
output "rds_cluster_instance_endpoints" {
description = "A list of all cluster instance endpoints"
value = aws_rds_cluster_instance.this.*.endpoint
}
Last updated