If you just arrived and want to understand what has been done so far, I recommend taking a step back and checking out the previous posts:
Terraform: Taking the Next Step – Building Infrastructure from Scratch on GCP
This post (and the next one) will probably be a bit more theoretical than I like, but let’s take an important step: organizing the project with best practices. We’ll better understand the use of variables, outputs – which we created previously. And we’ll take the opportunity to modularize the project a bit more.
What we’ll see today:
- How to declare and use variables correctly
- How to use outputs to facilitate integration and debugging
- Variable types in Terraform
- How to structure your project in a clean and scalable way
- First best practices for working with Terraform professionally
1. Variable types
In Terraform, we can use various types of variables, such as:
string: text valuenumber: numeric valuebool:trueorfalselist: list of values, e.g.:["dev", "staging", "prod"]map: key-value structure, e.g.:{ dev = "10.0.0.0/16", prod = "10.1.0.0/16" }object: more complex structure
List example:
variable "regions" {<br>type = list(string)<br>default = ["us-central1", "us-east1"]<br>}2. Outputs
Outputs are ways to export useful information after apply, such as IPs, resource names, or service account emails that were created in the process.
Let’s go to a practical example:
Example of outputs.tf in a module:
output "instance_ip" {
value = google_compute_instance.vm.network_interface[0].access_config[0].nat_ip
}How do we access this output in another module or environment:
If you use terraform_remote_state, you can do this, for example:
data "terraform_remote_state" "dev" {
backend = "gcs"
config = {
bucket = "my-terraform-states"
prefix = "dev/infrastructure"
}
}
resource "google_dns_record_set" "record" {
name = "app.dev.example.com."
type = "A"
ttl = 300
rrdatas = [data.terraform_remote_state.dev.outputs.instance_ip]
}This allows you to “chain” infrastructures: the output from the dev environment can feed prod, or one module can feed another.
This is useful for integrating with other modules, CI/CD tools or just to know what was created (as we did in the previous post)
3. Structure best practices
Minimal and clean structure:
Terraform/
├── main.tf
├── variables.tf
├── outputs.tf
├── terraform.tfvarsSome suggestions:
- Use the
variables.tffile only to declare variables, without values. - Keep sensitive values **outside the repository**, i.e., put the
.tfvarsin.gitignore. - Separate environments (dev, prod) with different files or using workspaces (we'll talk about this later)
4. Other important best practices
- Always use
terraform planbefore apply - Version Terraform (
.terraform-versionor inrequired_version– more at [Managing Terraform Versions](https://developer.hashicorp.com/terraform/tutorials/configuration-language/versions)) - Use remote backends for
state(we'll talk about this in the next post!) - Comment and document your code
- Start thinking about **modules** for reusable components (we'll talk about this soon too!)