Terraform in Drone CI is less about a specific "problem" and more about understanding how to integrate a declarative infrastructure-as-code tool into an automated workflow. The surprising truth is that Drone’s container-native approach actually makes running Terraform simpler than you might expect, provided you grasp how its execution model maps to Terraform’s state management and credential handling.

Let’s see it in action. Imagine you have a main.tf file like this, setting up a simple AWS S3 bucket:

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

resource "aws_s3_bucket" "example" {
  bucket = "my-drone-terraform-example-bucket-12345" # Must be globally unique
  acl    = "private"

  tags = {
    Environment = "Dev"
    ManagedBy   = "Terraform"
  }
}

And you want to deploy this using Drone. Your .drone.yml might look something like this:

kind: pipeline
type: docker
name: terraform-deploy

steps:
- name: init-configure
  image: hashicorp/terraform:1.5.7
  commands:
    - terraform init -backend-config="bucket=my-terraform-state-bucket-unique" -backend-config="key=terraform/state.tfstate" -backend-config="region=us-east-1"
  environment:
    AWS_ACCESS_KEY_ID:
      from_secret: aws_access_key_id
    AWS_SECRET_ACCESS_KEY:
      from_secret: aws_secret_access_key

- name: plan
  image: hashicorp/terraform:1.5.7
  commands:
    - terraform plan -out=tfplan
  environment:
    AWS_ACCESS_KEY_ID:
      from_secret: aws_access_key_id
    AWS_SECRET_ACCESS_KEY:
      from_secret: aws_secret_access_key

- name: apply
  image: hashicorp/terraform:1.5.7
  commands:
    - terraform apply -auto-approve tfplan
  environment:
    AWS_ACCESS_KEY_ID:
      from_secret: aws_access_key_id
    AWS_SECRET_ACCESS_KEY:
      from_secret: aws_secret_access_key
  when:
    branch: main

This pipeline defines three steps: init-configure, plan, and apply.

  1. init-configure: This step runs terraform init. The critical part here is the -backend-config arguments. We’re telling Terraform to use an S3 bucket (my-terraform-state-bucket-unique) to store its state file (terraform/state.tfstate) in us-east-1. This is essential for any CI/CD workflow. Without a remote backend, Terraform state would be stored locally within the ephemeral build container, lost after the pipeline runs, and impossible to coordinate across multiple runs or developers. The AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are pulled from Drone secrets, providing the necessary AWS credentials.

  2. plan: This step executes terraform plan, generating an execution plan and saving it to a file named tfplan. This step also requires AWS credentials.

  3. apply: This step runs terraform apply -auto-approve tfplan. It takes the plan generated in the previous step and applies it to your infrastructure. The -auto-approve flag is common in CI/CD to avoid interactive confirmation. This step is conditionally executed only when the pipeline runs on the main branch.

The mental model here is that each Drone step is an isolated container. Terraform commands are executed within these containers. The hashicorp/terraform image provides the necessary tooling. The key to making this work is:

  • Remote State: Always configure a remote backend (like S3, GCS, Azure Blob Storage, or Terraform Cloud) so that Terraform state is persisted outside the build agent.
  • Credentials: Inject cloud provider credentials securely via environment variables, using Drone’s secret management.
  • Workflow: Structure your pipeline logically – init, plan, apply. For more complex workflows, you might add steps for formatting (terraform fmt), validation (terraform validate), or even destroy operations.
  • Locking: Remote backends typically handle state locking automatically, preventing concurrent runs from corrupting your state.

What most people don’t realize is that Terraform’s init command is not just about downloading providers; it’s also about configuring the state backend. If you try to run terraform plan or apply without a properly configured remote backend in a CI environment, you’ll either fail due to missing state or, worse, end up with a local state file that’s immediately discarded, leading to Terraform wanting to recreate everything on the next run. The init step’s configuration is paramount.

The next concept to explore is managing different environments (dev, staging, prod) with Terraform within Drone, likely involving separate state files and potentially different variable sets.

Want structured learning?

Take the full Drone course →