Introduction
This article will detail how to use the Terraform Cloud/Enterprise API to create a new state version. This is used to upload a new Terraform state file to a workspace.
Problem
The Terraform state file can become corrupted, causing subsequent Terraform operations to produce unexpected results. This may render a workspace unusable until the Terraform state file is modified.
Cause
Here are some of the causes for a corrupted Terraform state file.
- A partially applied Terraform operation.
- Uploading the wrong state file using
terraform state push
. - Running an incorrect
terraform taint
orterraform import
operation. - An inadvertent Terraform CLI version change.
Solution
The following variables will be used throughout this solution. Please replace the value of these variables as necessary.
-
${HOSTNAME}
: The hostname of the Terraform Enterprise instance. Useapp.terraform.io
for Terraform Cloud. -
${TOKEN}
: The user API token for API authentication. -
${WORKSPACE_ID}
: The workspace ID of the workspace to create a new state version in.
The process to create a state version and upload a Terraform state file using the Terraform Cloud/Enterprise API is below.
-
Navigate to the desired workspace within Terraform Cloud/Enterprise.
-
Lock the desired workspace by navigating to Settings > Locking and clicking on the Lock button.
-
Retrieve the workspace ID of the desired workspace by navigating to Settings > General. This workspace ID is needed for the API calls below.
Note: The next two steps will guide you through downloading the workspace’s current state version. This state file can then be modified as needed, and used to create the new state version. You may skip these steps if you have already obtained the state file you wish to use. For example, if a workspace was unintentionally upgraded and you wish to roll back to a previous state version, you will need to identify and download the appropriate state file from the workspace.
-
Run the following command to retrieve the download URL for the current Terraform state file in use by the workspace. The API response will contain a JSON attribute named
hosted-state-download-url
which contains the URL where the Terraform state file can be downloaded.Shell:
$ curl \ --header "Authorization: Bearer ${TOKEN}" \ --header "Content-Type: application/vnd.api+json" \ "https://${HOSTNAME}/api/v2/workspaces/${WORKSPACE_ID}/current-state-version"
Powershell:
$WORKSPACE_ID = '<insert_your_workspace_id'
$TOKEN = '<insert_your_token>'
$HOSTNAME = '<tfe_fqdn>'
$params = @{
Uri = "https://${HOSTNAME}/api/v2/workspaces/${WORKSPACE_ID}/current-state-version"
Headers = @{ 'Authorization' = "Bearer $TOKEN"; 'Content-Type' = "application/vnd.api+json" }
Method = 'GET' } -
Using the value of
hosted-state-download-url
obtained previously, download the Terraform state file using the following command. Be sure to replace${URL}
with the value of thehosted-state-download-url
obtained previously. Here the Terraform state file is being saved as a file namedstate.tfstate
.Shell:
curl -L \
--header "Authorization: Bearer $TOKEN" \
--header "Content-Type: application/vnd.api+json" \
${URL} > state.tfstate
Powershell:$TOKEN = '<insert_your_token>'
$URL = '<hosted-state-download-url>'
$headers = @{
Authorization = "Bearer $TOKEN"
'Content-Type' = 'application/vnd.api+json'
}
$ Invoke-WebRequest -Uri $URL -Headers $headers -Method Get -OutFile 'state.tfstate' -
Modify the downloaded
state.tfstate
file as desired. Be sure to increment theserial
attribute by one. Take note of the values for thelineage
andserial
(post increment) attributes as they will be needed later. -
Compute the MD5 checksum of the
state.tfstate
file using the following command. Take note of the value as it will be needed later.Shell:
$ md5sum state.tfstate
Powershell:
$ certUtil -hashfile .\terraform.tfstate MD5
-
Compute the base64 content of the
state.tfstate
file using one of the following commands. Take note of the value as it will be needed later.macOS:
$ base64 state.tfstate
Shell:
$ base64 -w 0 state.tfstate
Powershell:
$ File1 = "c:\temp\terraform.tfstate" $ Content1 = get-content $File1 $ Bytes = [System.Text.Encoding]::UTF8.GetBytes($Content1) $ Encoded = [System.Convert]::ToBase64String($Bytes) $ Encoded | set-content ($File1 + ".b64") $ Write-Host "ENCODED: " $Encoded
-
Create a file named
payload.json
with the following content. Replace the values in<>
with the values computed in the earlier steps. Please note the value for theserial
attribute should not be enclosed in double quotes since it is a JSON number.{ "data": { "type":"state-versions", "attributes": { "serial": <SERIAL>, "md5": "<MD5_CHECKSUM>", "lineage": "<LINEAGE>", "state": "<BASE64_CONTENT>" } } }
-
Run the following command to create a state version and upload the newly modified Terraform state file.
Shell:
$ curl \ --header "Authorization: Bearer ${TOKEN}" \ --header "Content-Type: application/vnd.api+json" \ --request POST \ --data @payload.json \ "https://${HOSTNAME}/api/v2/workspaces/${WORKSPACE_ID}/state-versions"
Powershell:
$ TOKEN = 'insert-token-here' $ source = 'C:\payload.json' $ params = @{ Uri = 'https://${HOSTNAME}/api/v2/workspaces/${WORKSPACE_ID}/state-versions' Headers = @{ 'Authorization' = "Bearer $TOKEN"; 'Content-Type' = 'application/vnd.api+json' } Method = 'POST' InFile = $source } $ Invoke-RestMethod @params
-
Check to see if the new Terraform state file is listed in the workspace.
-
Unlock the workspace by navigating to Settings > Locking and clicking on the Unlock button.
-
Ensure the correct Terraform version is being used on the workspace.
Additional information
- The download statefile requires an authentication token in the latest TFE versions as documented here