Terraform: bash scripts to initialize and apply infrastructure

When managing infrastructure with Terraform across multiple environments and multiple workspaces the setup can become repetitive and error-prone. In these situations you could utilize Terraform bash scripts automation to initialize and apply infrastructure.

Is there a need for scripts?

Sometimes it is easy to forget necessary steps such as cleaning up local .terraform/ directory, accidentally run commands in the wrong workspace or use the wrong variable files for a given stage. Small missteps can lead to state mismatches, failed deployments or other unexpected infrastructure changes. You might get by without scripts, but having them ensures a smoother, more consistent workflow with fewer surprises.

The .terraform/ directory is local folder created by Terraform during terraform init. Terraform uses it to:

  • manage cached provider plugins and modules
  • record which workspace is currently active
  • record the last known backend configuration

If this directory already exists from a previous run and you switch to a different one it can cause:

  • conflicts or incorrect backend usage
  • terraform may think you’re in the wrong workspace
  • cached modules or providers may not match the current environment

This is the reason why deleting .terraform/ before reinitializing ensures a clean and accurate setup.

Using incorrect .tfvars file might cause misconfigured resources or even higher cost for example use more capacity for storage service on account that is used only for testing.

If you run terraform init with the wrong backend config you might point to another environment’s state file. That means changes meant for dev could update resources in qa.

After specific changes Terraform sometimes requires reinitialization, like switching backends, updating provider versions or changing module sources etc. If you skip reinitializing Terraform might use outdated plugins or stale config leading to unexpected errors or failed applies.

Let’s say you’re working on S3 changes intended for the dev environment, but you’re still in the qa workspace. If you apply your Terraform config using dev variables without switching to the correct dev workspace, those changes will be applied to the qa state — possibly even in the wrong AWS account or targeting unintended resources. This can lead to misconfigured infrastructure and a lot of confusion.

The Solution: Two Simple Scripts

Use case:
You’re managing a large infrastructure spread across multiple AWS accounts (e.g., dev, qa, prod). To keep things organized, you group related resources into separate Terraform workspaces — for example, all monitoring-related infrastructure lives in the monitoring workspace. Since multiple engineers may work on the same environment but focus on different resource areas, this setup allows them to collaborate without issues.

1. init.sh – Initialize Terraform the right way

This script does the following:

  • Accepts the target stage (dev, qa, main) as input
  • Optionally takes a workspace name (defaults to monitoring as example)
  • Removes any existing .terraform local state
  • Runs terraform init with the correct backend config
  • Selects or creates the appropriate workspace
#!/bin/bash
set -e

### IMPORTANT ###
# Setup correct AWS credentials.

### INPUTS ###
# Check for valid environment name
if [[ -z "$1" ]]; then
    echo "Error: Please provide an environment variable (dev, qa, prod) as the first argument."
    exit 1
fi
# Pass the targeted stage as the first input parameter to the script execution.
# e.g. for dev: ./init.sh dev
aws_stage="${1:?Input missing. Please pass an environment variable (dev, qa, prod) as first argument.}"
echo "Applying $aws_stage as target aws stage."

env_name="${2:-monitoring}"
echo "Using $env_name as terraform workspace."

# Remove preexisting local terraform state
if [ -d .terraform ]; then
  rm -rf .terraform
fi

terraform init \
  -input=false \
  -backend-config="../../stages/$env_name/$aws_stage.backend.config"

if ! terraform workspace select "$env_name"; then
  terraform workspace new "$env_name"
  echo "Created new workspace: $env_name"
else
  echo "Using existing workspace: $env_name"
fi

Script call:

./init.sh dev monitoring

Something like:

  -backend-config="../../stages/$env_name/$aws_stage.backend.config"

is not necessary needed but it adds a strong layer of safety when working across multiple environments. This way you dynamically load a specific backend config per environment which also ensures nice state isolation. You can find more here https://developer.hashicorp.com/terraform/language/backend
Your directory structure you could have something like this:

monitoring/
├── dev.backend.config
├── dev.tfvars
├── qa.backend.config
├── qa.tfvars
├── prod.backend.config
├── prod.tfvars
2. deploy.sh – Apply infrastructure

This script does the following:

  • Validates input – ensures you provide and environment as first argument
  • Sets a workspace name and use default one if not provided (easier to have default one if you have scripts per workspace)
  • Runs Terraform apply using correct *.tfvars file based on the environment and workspace
#!/usr/bin/env bash
set -euo pipefail

### IMPORTANT ###
# Setup correct AWS credentials.

### INPUTS ###
# Pass the targeted stage as the first input parameter to the script execution.
# e.g. for dev: ./deploy.sh dev
readonly aws_stage="$1"

# e.g. for dev: ./deploy.sh dev monitoring
readonly terraform_workspace="${2:-monitoring}"

if [ -z "$aws_stage" ]; then
  echo "Input missing. Please pass an environment variable (dev, qa, main) as first input."
  exit 1
fi
echo "Applying $aws_stage as target AWS stage."
echo ""

echo "Using $terraform_workspace as Terraform workspace."
echo ""

### MAIN ###

terraform apply \
-var-file="../../stages/${terraform_workspace}/${aws_stage}.tfvars"

Script call:

./deploy.sh dev monitoring

Why it works well

Every engineer runs the exact same commands with the same structure.

No need to remember backend paths, default workspaces, or variable files — it’s all built in.

New team members can get started faster. They just run the script and focus on the infrastructure itself.y

These scripts can easily be adapted into CI/CD pipelines (e.g., GitLab or GitHub Actions) without changing core logic. So you could have something like:

.monitoring_ci_script:
  script:
    - cd $TF_ROOT
    - sh ./init.sh $CI_COMMIT_BRANCH $WORKSPACE

Conclusion

In complex infrastructure setups, small mistakes can lead to big problems. By following the approach in “Terraform: Utilize bash scripts to initialize and apply infrastructure”, you can standardize workflows, reduce human error, and simplify collaboration across teams and environments. These scripts make switching between environments safer and faster, especially when managing multiple workspaces. Even though they’re simple, they bring consistency and confidence to every Terraform run. If you’re looking to improve reliability and speed, Terraform bash scripts automation is a practical step forward.

Buy Me A Coffee