When using Terraform, the state of your infrastructure is stored in a local file called tfstate. This is a very important file, so modifying it or loosing will leave you with an unusable Terraform configuration. You will probably have to re-import all your ressources manually.

There are some cases when you need to use Terraform from multiple devices for the same project. It can happen if a team works on a common Terraform project. They can also commit to same git repository, and trigger Terraform actions via some CI/CD system. Even in that case, the tfstate needs to be persistent across pipelines, so a third-party hosting service is required.

For that, Terraform has backends. In fact, by default it uses the local backend, which is the terraform.tfstate file in the project directory.

Available backends include S3, Google Cloud Storage, Swift, Consul, etcd, and more. You can also use backend for locking, to prevent conflicts.

In my case, I moved my Terraform project to GitLab CI, so that I can only plan and apply a committed change, from a single place.

On GitLab CI, you can run pipelines. These pipelines are composed of stages which are composed of jobs. You can make some data persistant across jobs and stages via artifacts, which I do for the .terraform folder and terraform plan output. However, there is no way to do the same across pipelines, and this is needed for the tfstate.

This is were a backend comes in. I am using an S3 bucket on Wasabi in order to store the tfstate file.

The reason I am doing this post is because by default the s3 backend is really made to be used on AWS's S3 service. However, It is possible to use any other S3-compatible service.

Here is how I set up my backend:

terraform {
  backend "s3" {
    bucket   = "my-bucket"
    key      = "my-key"
    region   = "us-east-1"
    endpoint = "s3.wasabisys.com"
    skip_credentials_validation = true
  }
}

You need to set your S3 credentials via the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables. These should be associated with an IAM user with write access to the bucket.

The 2 very important settings are endpoint, which I set to Wasabi's endpoint and skip_credentials_validation.

The latter was what took me the most time to find. By default, Terraform will try to validate these credentials via the AWS Security Token Service, which will of course fail if you are not using AWS.

After defining your backend, you can run terraform init and migrate your tfstate.