Terraform Naming Conventions & Best Practices: A hell-of-a-practical guide

Francesco Altomare Southern Europe Regional Manager @ GlobalDots
13 Min read

Modules naming conventions 

Based on Hashicorp documentations we should follow general naming conventions for  Terraform modules. 

Based on information above all Terraform modules should follow next ruleset All source code in git

Reduce your AWS costs by over 50%

Discover your Cloud Saving Potential – Answer just 5 simple questions. AppsFlyer, Playtika, Lufthansa, IBM, top leading companies are already using our FinOps services.

Reduce your AWS costs 
by over 50%
  • All modules follow naming convention terraform-<PROVIDER>-<NAME>. For  example terraform-aws-ec2, terraform-azure-vms, terraform-infoblox-dns etc All terraform modules should have Unit/Integration tests. For example Terratest  or Kitchen-Terraform or other tests framework but TerraTest is prefered test framework All terraform modules shouldn’t use provisioners. Provisioners are a Last Resort All terraform modules should be secure and follow vendor and hashicorp security best  practices. In another words you should use scanning tools like Checkov or tfsec All terraform modules should have right code style 
  • All terraform modules should follow Semantic Versioning
  • Use Snake Case for all resource names 
  • Declare all variables in variables.tf, including a description and type 
  • Declare all outputs in outputs.tf, including a description 
  • Always use relative paths and the file() helper 
  • Pin all modules and providers to a specific version or tag 
  • Prefer separate resources over inline blocks (e.g. aws_security_group_rule over  aws_security_group) 
  • Prefer variables.tf over terraform.tfvars to provide sensible defaults 
  • Terraform versions and provider versions should be pinned, as it’s not possible to safely  downgrade a state file once it has been used with a newer version of Terraform 

Terraform module structure 

All terraform modules should follow structure as shows above 

├── .gitignore
├── .markdownlint.json
├── .pre-commit-config.yaml
├── README.md
├── examples
│ ├── complete
│ │ ├── main.tf
│ │ ├── outputs.tf
│ │ ├── variables.tf
│ │ └── versions.tf
│ └── minimal
│ ├── main.tf
│ ├── outputs.tf
│ ├── variables.tf
│ └── versions.tf
├── main.tf
├── outputs.tf
├── test
│ ├── go.mod
│ ├── go.sum
│ └── terraform_module_gcp_dns_test.go
├── variables.tf
└── versions.tf

line 2: .gitignore – should contain all extra files. Lets keep git repository clear(required) line 3: .markdownlint.json – linting configuration for Markdown(optional)  line 4: .pre-commit-config.yaml – configuration for pre-commit framework(required) line 5: LICENSE file for explanations under which license this module 

line 6: README.md – Readme with examples of terraform module usage, requirements, input  parameters, outputs,full all important information and warning if applicable (required) line 7: VERSION – Versions history for module with release notes (required) line 8: examples – Examples for module should contain at least minimal(line 14) (required) line 19: main.tf – General module file. Contains resources,locals and data-sources to create all  resources (required) 

line 20: outputs.tf – Contains outputs from the resources created in main.tf (required) line 21: test – All unit/integration tests for Terraform module (required) 

line 25: variables.tf – contains declarations of variables used in main.tf (required) line 26: versions.tf – contains all versions for Terraform itself and for providers in module  (required) 

General best practices 

Resource/Data name 

Don’t repeat resource type, don’t use partially or completely  


resource “aws_instance” “webserver_aws_instance” {} 


resource “aws_instance” “webserver_instance” {} 


resource “aws_instance” “webserver” {} 

Usage of “this”

Resource name should be named this if there is no more descriptive and general name  available, or if resource module creates single resource of this type (eg, there is single resource 

of type aws_nat_gateway and multiple resources of typeaws_route_table, so  aws_nat_gateway should be named this and aws_route_table should have more descriptive  names – like private, public, database). 


Always use singular nouns for Resources name as well as for Data. Usage of “count “


resource “aws_instance” “webserver” { 

 ami = “123456” 

 count = “2” 


resource “aws_instance” “webserver” { 

 count = “2” 

 ami = “123456” 

Conditions in “count”

count = var.instances_per_subnet * length(module.vpc.private_subnets) or 

count = var.instance_count 

Placement of tags 

Tags for resources is required 


resource “aws_instance” “webserver” { 

 count = “2” 

 tags = { 

 Name = “…”



resource “aws_instance” “webserver” { 

 count = “2” 

 subnet_id = “…” 

 vpc_security_group_ids = “..” 

 tags = { 

 Name = “…” 



Use upstream module or provider variable names where applicable 

When writing a module that accepts variable inputs, make sure to use the same names as the  upstream to avoid confusion and ambiguity. 

Use all lower-case with underscores as separators 

Avoid introducing any other syntaxes commonly found in other languages such as CamelCase or  pascalCase. For consistency we want all variables to look uniform. This is also inline with  the HashiCorp naming conventions

Use positive variable names to avoid double negatives 

All variable inputs that enable/disable a setting should be  

formatted …._enabled (e.g. encryption_enabled). It is acceptable for default values to be  either false or true. 

Use feature flags to enable/disable functionality 

All modules should incorporate feature flags to enable or disable functionality. All feature flags  should end in _enabled and should be of type bool. 

Use description field for all inputs 

All variable inputs need a description field. When the field is provided by an upstream provider  (e.g. terraform-aws-provider), use the same wording as the upstream docs. 

Use sane defaults where applicable 

Modules should be as turnkey as possible. The default value should ensure the most secure  configuration (E.g. with encryption enabled).

Use variables for all secrets with nodefaultvalue 

All variable inputs for secrets must never define a default value. This ensures that terraform is  able to validate user input. The exception to this is if the secret is optional and will be generated  for the user automatically when left null or “” (empty). 

Use indented HEREDOC syntax 

Using <<-EOT (as opposed to <<EOT without the ) ensures the code can be indented inline with  the other code in the project. Note that EOT can be any uppercase string (e.g. CONFIG_FILE

Do not use HEREDOC for JSON, YAML or IAM Policies 

There are better ways to achieve the same outcome using terraform interpolations or resources For JSON, use a combination of a local and the jsonencode function. 

For YAML, use a combination of a local and the yamlencode function. 

For IAM Policy Documents, use the native iam_policy_document resource. 

Do not use long HEREDOC configurations 

Use instead the template_file resource and move the configuration to a separate template file.

 Use proper datatype 

Using proper datatypes in terraform makes it easier to validate inputs and document usage. 

  • Use null instead of empty strings (“”
  • Use bool instead of strings or integers for binary true/false 
  • Use string for freeform text 
  • Use object sparingly as it makes it harder to document and validate 


Use description field for all outputs 

All outputs must have a description set. The description should be based on (or adapted from) the  upstream terraform provider where applicable. Avoid simply repeating the variable name as the  output description. 

Use well-formatted snake case output names 

Avoid introducing any other syntaxes commonly found in other languages such as CamelCase or  pascalCase. For consistency we want all variables to look uniform. It also makes code more 

consistent when using outputs together with terraform remote_state to access those settings from  across modules. 

Never output secrets 

Secrets should never be outputs of modules. Rather, they should be written to secure storage  such as AWS Secrets Manager, AWS SSM Parameter Store with KMS encryption,  or S3 with KMS encryption at rest. Our preferred mechanism on AWS is using the SSM Parameter  Store. Values written to SSM are easily retrieved by other terraform modules, or even on the  command-line using tools like chamber by Segment.io

We are very strict about this in “root” modules (or the top-most module), because these sensitive  outputs are easily leaked in CI/CD pipelines (see tfmask for masking secrets in output only as a  last resort). We are less sensitive to this in modules that are typically nested inside of other  modules. 

Rather than outputting a secret, you may output plain text indicating where the secret is stored,  for example RDS master password is in SSM parameter /rds/master_password. You may also  want to have another output just for the key for the secret in the secret store, so the key is  available to other programs which may be able to retrieve the value given the key. 

Use symmetrical names 

We prefer to keep terraform outputs symmetrical as much as possible with the upstream resource  or module, with exception of prefixes. This reduces the amount of entropy in the code or possible  ambiguity, while increasing consistency. Below is an example of what *not to do. The expected  output name is user_secret_access_key. This is because the other IAM user outputs in the  upstream module are prefixed with user_, and then we should borrow the upstream’s output  name of secret_access_key to become user_secret_access_key for consistency. 


Use Remote state 

For Terraform it’s required to use remote state 

Use backend with support for state locking 

We recommend using the S3 backend with DynamoDB for state locking or similar solutions Use encrypted S3 bucket with versioning, encryption and strict IAM policies 

We recommend not commingling state in the same bucket. This could cause the state to get  overridden or compromised. Note, the state contains cached values of all outputs. Wherever  possible, keep stages 100% isolated with physical barriers (separate buckets, separate  organizations)

Use Versioning on State Bucket 

For Terraform it’s required to use versioning for remote state 

Terraform Toolset 



Checkov is a static code analysis tool for infrastructure-as-code. 

It scans cloud infrastructure provisioned using Terraform, Terraform  

plan, Cloudformation, Kubernetes, Dockerfile, Serverless or ARM Templates and detects  security and compliance misconfigurations using graph-based scanning. 

Checkov also powers Bridgecrew, the developer-first platform that codifies and streamlines  cloud security throughout the development lifecycle. Bridgecrew identifies, fixes, and prevents  misconfigurations in cloud resources and infrastructure-as-code files. 



tfsec uses static analysis of your terraform templates to spot potential security issues. Now with  terraform CDK support. 



terraform-compliance is a lightweight, security and compliance focused test framework against  terraform to enable negative testing capability for your infrastructure-as-code. 



Terrascan is a static code analyzer for Infrastructure as Code. Terrascan allow you to: 

  • seamlessly scan infrastructure as code for misconfigurations 
  • monitor provisioned cloud infrastructure for configuration changes that introduce posture  drift, and enables reverting to a secure posture. 
  • Detect security vulnerabilities and compliance violations. 
  • Mitigate risks before provisioning cloud native infrastructure.
  • Offers flexibility to run locally or integrate with your CI\CD. 



Regula is a tool that evaluates CloudFormation and Terraform infrastructure-as-code for  potential AWS, Azure, and Google Cloud security and compliance violations prior to  deployment. 




Find security vulnerabilities, compliance issues, and infrastructure misconfigurations early in the  development cycle of your infrastructure-as-code with KICS by Checkmarx. 





A Pluggable Terraform Linter 


TFLint is a framework and each feature is provided by plugins, the key features are as follows: 

  • Find possible errors (like illegal instance types) for Major Cloud providers  (AWS/Azure/GCP). 
  • Warn about deprecated syntax, unused declarations. 
  • Enforce best practices, naming conventions. 



A command line tool to validate configuration files using rules specified in YAML. The  configuration files can be one of several formats: Terraform, JSON, YAML, with support for  Kubernetes. There are built-in rules provided for Terraform, and custom files can be used for  other formats.



Hashicorp articles 

Testing hashicorp terraform 

Testing experiment for Terraform 


Chef InSpec is an open-source framework for testing and auditing your applications and  infrastructure. Chef InSpec works by comparing the actual state of your system with the desired  state that you express in easy-to-read and easy-to-write Chef InSpec code. Chef InSpec detects  violations and displays findings in the form of a report, but puts you in control of remediation. 

Moving Security and Sanity Left by Testing Terraform with InSpec 

Hashicorp blog 



Terratest is a Go library that provides patterns and helper functions for testing infrastructure,  with 1st-class support for Terraform, Packer, Docker, Kubernetes, AWS, GCP, and more. Docs 



Ones again but more about BDD testing 



The creation of rspec-terraform was initially intended to smooth the creation and sharing of  common Terraform modules. Some sort of basic testing would ensure a stable and clearly  defined interface for each module. 



A declarative test framework for Terraform 




A framework for managing and maintaining multi-language pre-commit hooks. Main site 

Example of usage 


From pip 

pip install pre-commit 

Non-administrative installation: 

curl https://pre-commit.com/install-local.py | python – In a python project, add the following to your requirements.txt (or requirements-dev.txt): pre-commit 

From brew 

brew install pre-commit 


cd tf/module/path 

Create .pre-commit-config.yaml in module path 

Simple example for terraform 

#pre commit framework 



 python: python3 


 – repo: git://github.com/pre-commit/pre-commit-hooks  rev: v3.2.0 


 – id: check-json 

 – id: check-merge-conflict 

 – id: trailing-whitespace 

 – id: end-of-file-fixer 

 – id: check-yaml 

 – id: check-added-large-files 

 – id: pretty-format-json 


 – –autofix 

 – id: detect-aws-credentials 

 – id: detect-private-key 

 – repo: git://github.com/Lucas-C/pre-commit-hooks  rev: v1.1.9 


 – id: forbid-tabs 


 – python 

 – javascript 

 – dtd 

 – markdown 

 – makefile 

 – xml 

 exclude: binary|\.bin$ 

 – repo: git://github.com/jameswoolfenden/pre-commit-shell  rev: 0.0.2 


 – id: shell-lint 

 – repo: git://github.com/igorshubovych/markdownlint-cli  rev: v0.23.2 


 – id: markdownlint 

 – repo: git://github.com/adrienverge/yamllint  rev: v1.24.2 


 – id: yamllint 

 name: yamllint 

 description: This hook runs yamllint.  entry: yamllint 

 language: python 

 types: [file, yaml] 

 – repo: git://github.com/jameswoolfenden/pre-commit  rev: v0.1.33 


 – id: terraform-fmt 

 – id: checkov-scan 

 language_version: python3.7 

 – id: tf2docs 

 language_version: python3.7 

Now you can init git if you don’t init yet 

git init 

Now need to install hooks from .pre-commit-config.yaml run


pre-commit install 

If you want run pre-commit without commiting you changes 

git add . 

pre-commit run -a 

After running command above you could see output like 


JSON………………………………………………………Passed Check for merge  

conflicts…………………………………………Passed Trim Trailing  

Whitespace………………………………………….Passed Fix End of  

Files…………………………………………………Passed Check  

Yaml………………………………………………………Passed Check for added large  


Pretty format  

JSON……………………………………………….Passed Detect AWS  

Credentials……………………………………………Passed Detect Private  

Key……………………………………………….Passed No-tabs  

checker………………………………………………….Passed Shell Syntax Check……………………………..(no files to  check)Skipped 

markdownlint…………………………………………………….Pass ed 

yamllint………………………………………………………..Pass ed 


fmt……………………………………………………Passed checkov…………………………………………………………Pass ed 

Terraform version manager 


Terraform version manager inspired by rbenv 



The tfswitch command line tool lets you switch between different versions of terraform. If you  do not have a particular version of terraform installed, tfswitch lets you download the version  you desire. The installation is minimal and easy. Once installed, simply select the version you  require from the dropdown and start using terraform. 




A Terrafile is a simple YAML config that gives you a single, convenient location that lists all your  external module dependencies. 

The idea is modelled on similar patterns in other languages – e.g. Ruby with  its Gemfile (technically provided by the bundler gem). 

Additionally, tfile supports modules from the Terraform Registry, as well as local modules and  from git. 



 source: “GoogleCloudPlatform/lb-http/google” 

 version: “4.5.0” 


source: https://github.com/terraform-aws-modules/terraform-aws-vpc.git version: v2.64.0 

provider: aws 



The Terrafile is where you can define additional modules to add to your Terraspace project. The  modules can be from your own git repositories, other git repositories, or the Terraform Registry. 




Terraspace is a Terraform Framework that optimizes for infrastructure-as-code happiness. It  provides an organized structure, conventions over configurations, keeps your code DRY, and  adds convenient tooling. Terraspace makes working with Terraform easier and more fun.




Terragrunt is a thin wrapper that provides extra tools for keeping your configurations DRY,  working with multiple Terraform modules, and managing remote state. 




Pretf is a completely transparent, drop-in Terraform wrapper that generates Terraform  configuration with Python. It requires no configuration and no changes to standard Terraform  projects to start using it. 


CDK for Terraform 

CDK (Cloud Development Kit) for Terraform allows developers to use familiar programming  languages to define cloud infrastructure and provision it through HashiCorp Terraform. 


Module generators 


Scaffolding / Boilerplate generator for new Terraform module projects 


Terraform code generators 


Generate CloudFormation / Terraform / Troposphere templates from your existing AWS  resources 



A CLI tool that generates tf/json and tfstate files based on existing infrastructure (reverse  Terraform). 



Blast Radius 

Blast Radius is a tool for reasoning about Terraform dependency graphs with interactive  visualizations. 




Terraform is intricate but extremely powerful if you know all the ins-and-outs.

Our job at GlobalDots is to do that in order to provide the best possible DevOps service to our fast-growing business customers.

Contact us to learn how we can help you grow lean and smart, while keeping your precious resources focused on coding. 

Latest Articles

How Justt Saved $100K Yearly with IaC

Justt is a chargeback mitigation startup based in Tel Aviv. Chargebacks, as defined, are demands by a credit card provider for a retailer to reimburse losses on fraudulent or disputed transactions. Justt’s objective is to assist merchants worldwide in combating false chargebacks using its proprietary artificial intelligence technology.

22nd February, 2024
On-Demand Webinar: Securing Content on AWS with Okta

Not implementing OpenID Connect properly in AWS can lead to various consequences, including security breaches, unauthorized access to sensitive information, and compromised user data. However, these risks can be avoided when OpenID Connect is configured carefully, as it is designed to provide a secure and seamless way to authenticate users and control access to protected […]

Valerii Vainkop DevOps Team Leader @ GlobalDots
16th April, 2023
On-Demand Webinar: Testing IaS – How to Solve the Common Challenges

Are you struggling to effectively test your Terraform infrastructure code? Even the best plans can fail, resulting in half-formed infrastructure that can have serious consequences for your business, such as lost revenue or damage to your reputation due to downtime, security vulnerabilities, operational inefficiencies, and difficulties scaling. In this webinar, we will introduce an innovative […]

Sergey Bulavintsev DevOps Engineer
27th February, 2023

Unlock Your Cloud Potential

Schedule a call with our experts. Discover new technology and get recommendations to improve your performance.

Unlock Your Cloud Potential