# State backends

Where the Terraform state lives in Buddy pipelines: a Buddy artifact with locking, S3 or GCS with integration credentials, or your own backend.

Terraform stores what it manages in a **state**. Without a persistent state, every run would create the infrastructure from scratch. Each Terraform action has a `backend` setting that tells Buddy where the state is kept:

| `backend.type` | Where the state is | What Buddy adds |
| :--- | :--- | :--- |
| `ARTIFACT` | A Buddy artifact | The backend configuration and credentials |
| `AMAZON` | An S3 backend declared in your code | AWS credentials from an integration |
| `GOOGLE` | A GCS backend declared in your code | Google credentials from an integration |
| `CUSTOM` (default) | The backend declared in your code | Nothing |

<Hint type="warning">

Set the **same backend** in every action that works on one state: Plan, Apply, and Drift Detection. Otherwise Apply works on a different state than the one that was planned.

</Hint>

## ARTIFACT

The state is stored in a Buddy artifact. You don't need a bucket, a lock table, or any credentials.

### Requirements

- The artifact and its version **must already exist**. Buddy doesn't create them, and the action fails with *not found* if either is missing.
- The artifact must be of type **Bucket**, with **Access control** set to **Buddy Auth**.
- The pipeline needs **read-write** access to the artifact.
- Your code **must not declare a `backend` block**.

```yaml
backend:
  type: ARTIFACT
  artifact: infra-state
  artifact_version: prod
```

![Backend tab with Buddy Artifact selected](/docs/terraform/terraform-plan-backend-artifact.png 640x343)

### How it works

Before running the tool, Buddy writes `buddy_backend.tf` to the working directory:

```hcl
terraform {
  backend "http" {}
}
```

It also sets the HTTP backend variables:

| Variable | Value |
| :--- | :--- |
| `TF_HTTP_ADDRESS`, `TF_HTTP_LOCK_ADDRESS`, `TF_HTTP_UNLOCK_ADDRESS` | `<artifact version URL>/tf.tfstate` |
| `TF_HTTP_USERNAME` | The pipeline ID |
| `TF_HTTP_PASSWORD` | A token of the pipeline run |

One artifact version holds one state file, `tf.tfstate`, which is overwritten on every apply. To keep separate states, for example per environment, use separate versions (`dev`, `staging`, `prod`) or separate artifacts.

### Locking

The artifact backend supports state locking. While one action holds the lock, another one working on the same state waits or fails, depending on `-lock-timeout` in `additional_args`.

### Size limit

A state file can be up to **2 GB**. To raise the limit, contact [support](/contact).

### Moving an existing state to an artifact

Buddy doesn't migrate state automatically. To move an existing state:

1. Make sure nothing is running against the old state.
2. Download it from the old backend: `terraform state pull > tf.tfstate`.
3. Upload `tf.tfstate` to the root of the artifact version - in the UI or with the [REST API](/docs/api/artifacts/versions/content/upload.md).
4. Remove the `backend` block from your code and set `backend.type` to `ARTIFACT`.
5. Run a Plan. If the state was moved correctly, it shows no changes.

### State name in the Terraform CLI action

In the [Terraform CLI action](/docs/terraform/terraform-cli.md), `state_name` changes the file name to `<state_name>.tfstate`. Plan, Apply, and Drift Detection always use `tf.tfstate`.

## AMAZON

Your code declares an `s3` backend. Buddy exports the credentials of the selected [AWS integration](/docs/integrations/amazon-web-services/amazon.md) to the container:

```hcl
terraform {
  backend "s3" {
    bucket = "my-terraform-state"
    key    = "prod/terraform.tfstate"
    region = "eu-central-1"
  }
}
```

```yaml
backend:
  type: AMAZON
  integration: my-aws
  region: eu-central-1
```

The exported variables are `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`, and `AWS_DEFAULT_REGION`, plus `AWS_ACCOUNT_ID`, `AWS_USER`, and `AWS_USER_ARN` for information. The AWS provider reads the same variables, so the integration also authenticates your AWS resources. The integration can use [OIDC](/docs/integrations/amazon-web-services/oidc.md) instead of static keys.

## GOOGLE

Your code declares a `gcs` backend. Buddy writes the key of the selected [Google Service Account integration](/docs/integrations/google-cloud/google-cloud.md) to a file and sets `GOOGLE_CREDENTIALS`. The Google provider reads it too.

```yaml
backend:
  type: GOOGLE
  integration: my-gcp
```

## CUSTOM

Buddy leaves the backend to your code. Use it for any backend Terraform supports, for example `azurerm`, `http`, `pg`, `remote`, or S3 with credentials you provide yourself. Pass the credentials as [variables](/docs/terraform/credentials-and-variables.md), for example `ARM_CLIENT_ID` and `ARM_CLIENT_SECRET` for Azure.


---
Original source: https://buddy.works/docs/terraform/state