cd ..
CI/CD

Automating Terraform validation using scripts within GitHub Actions workflows

Automating Terraform validation using scripts within GitHub Actions workflows

When we start creating pipelines in GitHub Actions, it’s common to put all commands directly inside the YAML file.

While this works, as the pipeline grows, it becomes difficult to maintain.

A commonly used alternative is to move all the logic into a Shell Script, leaving the workflow solely responsible for executing that script.

This pattern improves project organization and facilitates reuse across different pipelines.

Project structure

A simple example would be:

.
├── .github
│   └── workflows
│       └── testa-terraform.yml
├── infra
│   ├── provider.tf
│   ├── variables.tf
│   └── ec2.tf
└── scripts
    └── valida-tf.sh

Each file has a specific responsibility.


Configuring the Provider

The first step is to inform which provider will be used.

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

Creating an instance

Then we can declare a simple resource.

resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = var.instance_type

  tags = {
    Name = "HelloWorld"
  }
}

Declaring variables

Separating variables greatly facilitates code reuse.

variable "ami_id" {
  type    = string
  default = "ami-xxxxxxxx"
}

variable "instance_type" {
  type    = string
  default = "t3.micro"
}

Creating the validation script

Instead of putting all commands inside the workflow, we can create a Bash file.

#!/usr/bin/env bash

set -euo pipefail

terraform init -backend=false

terraform validate

The command

set -euo pipefail

is an excellent practice because:

Installing Terraform during the pipeline

If the runner does not have Terraform installed, we can download it automatically.

Simplified example:

curl -L "$DOWNLOAD_URL" -o terraform.zip

unzip terraform.zip

export PATH="$HOME/bin:$PATH"

This way, we don’t depend on a prior installation.

Running the validation

After initializing the project, simply validate the syntax.

terraform init -backend=false

terraform validate

The parameter

-backend=false

is very useful during tests because it avoids configuring a remote backend just to validate the project structure.

Returning the result to GitHub Actions

Another interesting practice is to report the execution result using the special GitHub Actions file.

When everything works:

echo "tf_result=success" >> "$GITHUB_OUTPUT"

If an error occurs:

echo "tf_result=failure" >> "$GITHUB_OUTPUT"

exit 1

This way, other pipeline steps can use this information.

Simplified workflow

The YAML becomes extremely small.

name: Testa Terraform

on:
  workflow_dispatch:

jobs:
  valida:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Permitir execução
        run: chmod +x scripts/valida-tf.sh

      - name: Executar validação
        run: ./scripts/valida-tf.sh

Notice that virtually all logic has moved out of the workflow.

Advantages of this approach

Among the main benefits are:

Conclusion

Separating pipeline logic into Bash scripts is a common practice in DevOps projects.

GitHub Actions remains responsible for pipeline orchestration, while the Shell Script concentrates all execution logic.

This organization makes the code cleaner, facilitates local testing, and allows reusing the same script in different pipelines without duplicating commands.

The larger the project, the greater the benefit of this separation tends to be.

References

What did you think?