1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
|
---
stage: Deploy
group: Environments
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments
---
# GitLab-managed Terraform state
DETAILS:
**Tier:** Free, Premium, Ultimate
**Offering:** GitLab.com, Self-managed, GitLab Dedicated
> - Support for state names that contain periods introduced in GitLab 15.7 [with a flag](../../../administration/feature_flags.md) named `allow_dots_on_tf_state_names`. Disabled by default.
> - Support for state names that contain periods [generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/385597) in GitLab 16.0. Feature flag `allow_dots_on_tf_state_names` removed.
Terraform uses state files to store details about your infrastructure configuration.
With Terraform remote [backends](https://www.terraform.io/language/settings/backends/configuration),
you can store the state file in a remote and shared store.
GitLab provides a [Terraform HTTP backend](https://www.terraform.io/language/settings/backends/http)
to securely store your state files with minimal configuration.
The Terraform state backend provides automatic versioning and encryption of the state files managed by the GitLab instance.
WARNING:
**Disaster recovery planning**
Terraform state files are encrypted with the lockbox Ruby gem when they are at rest on disk and in object storage with a key derived from the [db_key_base application setting](../../../development/application_secrets.md#secret-entries).
[To decrypt a state file, GitLab must be available](https://gitlab.com/gitlab-org/gitlab/-/issues/335739).
If it is offline, and you use GitLab to deploy infrastructure that GitLab requires (like virtual machines,
Kubernetes clusters, or network components), you cannot access the state file easily or decrypt it.
Additionally, if GitLab serves up Terraform modules or other dependencies that are required to bootstrap GitLab,
these will be inaccessible. To work around this issue, make other arrangements to host or back up these dependencies,
or consider using a separate GitLab instance with no shared points of failure.
## Prerequisites
For self-managed GitLab, before you can use GitLab for your Terraform state files:
- An administrator must [set up Terraform state storage](../../../administration/terraform_state.md).
- You must enable the **Infrastructure** menu for your project. Go to **Settings > General**,
expand **Visibility, project features, permissions**, and under **Infrastructure**, turn on the toggle.
## Initialize a Terraform state as a backend by using GitLab CI/CD
Prerequisites:
- To lock, unlock, and write to the state by using `terraform apply`, you must have at least the Maintainer role.
- To read the state by using `terraform plan -lock=false`, you must have at least the Developer role.
WARNING:
Like any other job artifact, Terraform plan data is viewable by anyone with the Guest role on the repository.
Neither Terraform nor GitLab encrypts the plan file by default. If your Terraform `plan.json` or `plan.cache`
files include sensitive data like passwords, access tokens, or certificates, you should
encrypt the plan output or modify the project visibility settings. You should also **disable**
[public pipelines](../../../ci/pipelines/settings.md#change-pipeline-visibility-for-non-project-members-in-public-projects)
and set the [artifact's public flag to false](../../../ci/yaml/index.md#artifactspublic) (`public: false`).
This setting ensures artifacts are accessible only to GitLab administrators and project members with at least the Reporter role.
To configure GitLab CI/CD as a backend:
1. In your Terraform project, in a `.tf` file like `backend.tf`,
define the [HTTP backend](https://developer.hashicorp.com/terraform/language/settings/backends/http):
```hcl
terraform {
backend "http" {
}
}
```
1. In the root directory of your project repository, create a `.gitlab-ci.yml` file. Use the
[OpenTofu CI/CD component](https://gitlab.com/components/opentofu) to from your `.gitlab-ci.yml`. Follow the [Terraform template recipes](terraform_template_recipes.md) documentation if you prefer using Terraform instead of OpenTofu.
1. Push your project to GitLab. This action triggers a pipeline, which
runs the `gitlab-tofu init`, `gitlab-tofu validate`, and
`gitlab-tofu plan` commands.
1. Trigger the manual `deploy` job from the previous pipeline, which runs `gitlab-tofu apply` command, to provision the defined infrastructure.
The output from the above commands should be viewable in the job logs.
The `gitlab-tofu`/`gitlab-terraform` CLI is a wrapper around the `tofu`/`terraform` CLI, respectively. For more information,
see [GitLab Terraform helpers](gitlab_terraform_helpers.md),
or [view the source code of `gitlab-terraform`](https://gitlab.com/gitlab-org/terraform-images/-/blob/master/src/bin/gitlab-terraform.sh).
### Customizing your Terraform environment variables
You can use [Terraform HTTP configuration variables](https://www.terraform.io/language/settings/backends/http#configuration-variables) when you define your CI/CD jobs.
To customize your `init` and override the Terraform configuration,
use environment variables instead of the `init -backend-config=...` approach.
When you use `-backend-config`, the configuration is:
- Cached in the output of the `plan` command.
- Usually passed forward to the `apply` command.
This configuration can lead to problems like [being unable to lock the state files in CI jobs](troubleshooting.md#cant-lock-terraform-state-files-in-ci-jobs-for-terraform-apply-with-a-previous-jobs-plan).
## Access the state from your local machine
You can access the GitLab-managed Terraform state from your local machine.
WARNING:
On clustered deployments of GitLab, you should not use local storage.
A split state can occur across nodes, making subsequent Terraform executions
inconsistent. Instead, use a remote storage resource.
1. Ensure the Terraform state has been
[initialized for CI/CD](#initialize-a-terraform-state-as-a-backend-by-using-gitlab-cicd).
1. Copy a pre-populated Terraform `init` command:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Operate > Terraform states**.
1. Next to the environment you want to use, select **Actions**
(**{ellipsis_v}**) and select **Copy Terraform init command**.
1. Open a terminal and run this command on your local machine.
## Migrate to a GitLab-managed Terraform state
Terraform supports copying the state when the backend changes or is
reconfigured. Use these actions to migrate from another backend to
GitLab-managed Terraform state.
You should use a local terminal to run the commands needed for migrating to GitLab-managed Terraform state.
The following example demonstrates how to change the state name. The same workflow is needed to migrate to GitLab-managed Terraform state from a different state storage backend.
You should run these commands [on your local machine](#access-the-state-from-your-local-machine).
### Set up the initial backend
```shell
PROJECT_ID="<gitlab-project-id>"
TF_USERNAME="<gitlab-username>"
TF_PASSWORD="<gitlab-personal-access-token>"
TF_ADDRESS="https://gitlab.com/api/v4/projects/${PROJECT_ID}/terraform/state/old-state-name"
terraform init \
-backend-config=address=${TF_ADDRESS} \
-backend-config=lock_address=${TF_ADDRESS}/lock \
-backend-config=unlock_address=${TF_ADDRESS}/lock \
-backend-config=username=${TF_USERNAME} \
-backend-config=password=${TF_PASSWORD} \
-backend-config=lock_method=POST \
-backend-config=unlock_method=DELETE \
-backend-config=retry_wait_min=5
```
```plaintext
Initializing the backend...
Successfully configured the backend "http"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
re-run this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
```
### Change the backend
Now that `terraform init` has created a `.terraform/` directory that knows where
the old state is, you can tell it about the new location:
```shell
TF_ADDRESS="https://gitlab.com/api/v4/projects/${PROJECT_ID}/terraform/state/new-state-name"
terraform init \
-migrate-state \
-backend-config=address=${TF_ADDRESS} \
-backend-config=lock_address=${TF_ADDRESS}/lock \
-backend-config=unlock_address=${TF_ADDRESS}/lock \
-backend-config=username=${TF_USERNAME} \
-backend-config=password=${TF_PASSWORD} \
-backend-config=lock_method=POST \
-backend-config=unlock_method=DELETE \
-backend-config=retry_wait_min=5
```
```plaintext
Initializing the backend...
Backend configuration changed!
Terraform has detected that the configuration specified for the backend
has changed. Terraform will now check for existing state in the backends.
Acquiring state lock. This may take a few moments...
Do you want to copy existing state to the new backend?
Pre-existing state was found while migrating the previous "http" backend to the
newly configured "http" backend. No existing state was found in the newly
configured "http" backend. Do you want to copy this state to the new "http"
backend? Enter "yes" to copy and "no" to start with an empty state.
Enter a value: yes
Successfully configured the backend "http"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
```
If you type `yes`, it copies your state from the old location to the new
location. You can then go back to running it in GitLab CI/CD.
## Use your GitLab backend as a remote data source
You can use a GitLab-managed Terraform state backend as a
[Terraform data source](https://www.terraform.io/language/state/remote-state-data).
1. In your `main.tf` or other relevant file, declare these variables. Leave the values empty.
```hcl
variable "example_remote_state_address" {
type = string
description = "Gitlab remote state file address"
}
variable "example_username" {
type = string
description = "Gitlab username to query remote state"
}
variable "example_access_token" {
type = string
description = "GitLab access token to query remote state"
}
```
1. To override the values from the previous step, create a file named `example.auto.tfvars`. This file should **not** be versioned in your project repository.
```plaintext
example_remote_state_address = "https://gitlab.com/api/v4/projects/<TARGET-PROJECT-ID>/terraform/state/<TARGET-STATE-NAME>"
example_username = "<GitLab username>"
example_access_token = "<GitLab personal access token>"
```
1. In a `.tf` file, define the data source by using [Terraform input variables](https://www.terraform.io/language/values/variables):
```hcl
data "terraform_remote_state" "example" {
backend = "http"
config = {
address = var.example_remote_state_address
username = var.example_username
password = var.example_access_token
}
}
```
- **address**: The URL of the remote state backend you want to use as a data source.
For example, `https://gitlab.com/api/v4/projects/<TARGET-PROJECT-ID>/terraform/state/<TARGET-STATE-NAME>`.
- **username**: The username to authenticate with the data source. If you are using
a [personal access token](../../profile/personal_access_tokens.md) for
authentication, this value is your GitLab username. If you are using GitLab CI/CD, this value is `'gitlab-ci-token'`.
- **password**: The password to authenticate with the data source. If you are using a personal access token for
authentication, this value is the token value (the token must have the **API** scope).
If you are using GitLab CI/CD, this value is the contents of the `${CI_JOB_TOKEN}` CI/CD variable.
Outputs from the data source can now be referenced in your Terraform resources
using `data.terraform_remote_state.example.outputs.<OUTPUT-NAME>`.
To read the Terraform state in the target project, you need at least the Developer role.
## Manage Terraform state files
To view Terraform state files:
1. On the left sidebar, select **Search or go to** and find your project.
1. Select **Operate > Terraform states**.
[An epic exists](https://gitlab.com/groups/gitlab-org/-/epics/4563) to track improvements to this UI.
### Manage individual Terraform state versions
Individual state versions can be managed using the GitLab REST API.
If you have at least the Developer role, you can retrieve state versions by using their serial number::
```shell
curl --header "Private-Token: <your_access_token>" "https://gitlab.example.com/api/v4/projects/<your_project_id>/terraform/state/<your_state_name>/versions/<version-serial>"
```
If you have at least the Maintainer role, you can remove state versions by using their serial number:
```shell
curl --header "Private-Token: <your_access_token>" --request DELETE "https://gitlab.example.com/api/v4/projects/<your_project_id>/terraform/state/<your_state_name>/versions/<version-serial>"
```
### Remove a state file
If you have at least the Maintainer role, you can remove a state file.
1. On the left sidebar, select **Operate > Terraform states**.
1. In the **Actions** column, select **Actions** (**{ellipsis_v}**) and then **Remove state file and versions**.
### Remove a state file by using the API
You can remove a state file by making a request to the REST API using a personal access token:
```shell
curl --header "Private-Token: <your_access_token>" --request DELETE "https://gitlab.example.com/api/v4/projects/<your_project_id>/terraform/state/<your_state_name>"
```
You can also use [CI/CD job token](../../../ci/jobs/ci_job_token.md) and basic authentication:
```shell
curl --user "gitlab-ci-token:$CI_JOB_TOKEN" --request DELETE "https://gitlab.example.com/api/v4/projects/<your_project_id>/terraform/state/<your_state_name>"
```
You can also use [the GraphQL API](../../../api/graphql/reference/index.md#mutationterraformstatedelete).
## Related topics
- [Troubleshooting GitLab-managed Terraform state](troubleshooting.md)
- [Sample project: Terraform deployment of AWS EC2 instance in a custom VPC](https://gitlab.com/gitlab-org/configure/examples/gitlab-terraform-aws)
|