{% extends "base.html" %} {% import "repo_tabs.html" as m %} {% block title %}Terraform state ยท {{ repo.owner }}/{{ repo.name }}{% endblock %} {% block bodyattrs %} data-repo="{{ repo.url }}"{% endblock %} {% block tabs %}{% call m::tabs(repo, "terraform") %}{% endblock %} {% block content %}

Terraform state {{ states.len() }}

The hub keeps this repository's Terraform and OpenTofu state, sealed at rest, with its last {{ crate::terraform::VERSIONS_KEPT }} versions, outside the repository's history. Point a configuration at it with this backend block, naming the state in place of NAME (a repository may keep several):

terraform {
  backend "http" {
    address        = "{{ shell.base_url }}/api/v1/repos/{{ repo.owner }}/{{ repo.name }}/terraform/state/NAME"
    lock_address   = "{{ shell.base_url }}/api/v1/repos/{{ repo.owner }}/{{ repo.name }}/terraform/state/NAME"
    unlock_address = "{{ shell.base_url }}/api/v1/repos/{{ repo.owner }}/{{ repo.name }}/terraform/state/NAME"
    username       = "flow"
  }
}

From a pipeline

A job uses it with an identity token of this hub's, so it needs no cloud credential for the state. Give the job identity "{{ shell.base_url }}/terraform" {% raw %}{{% endraw %} mount-path "/run/secrets/tf" {% raw %}}{% endraw %}, run export TF_HTTP_PASSWORD=$(cat /run/secrets/tf) before tofu init, and let its sandbox reach this hub (network "egress" {% raw %}{{% endraw %} allow "{{ shell.base_url|host }}" {% raw %}}{% endraw %}, beside whatever else it reaches).

From your machine

Set TF_HTTP_PASSWORD to one of your tokens with repo:write, as someone with the write role here. Reading a state needs that too: states hold secrets.

States

{% if let Some(n) = notice %}

{{ n }}

{% endif %} {% if let Some(e) = error %}

{{ e }}

{% endif %} {% if !repo.can(crate::auth::Access::Write) %}

The states are shown to people who can write to this repository.

{% else if states.is_empty() %}

No state yet: the first tofu apply through the backend makes one.

{% else %}
{% for v in states %}{% endfor %}
StateSerialLast writtenLockVersions kept
{{ v.s.name }}{% if v.s.body.is_empty() %}deleted{% else %}{{ v.s.serial }}{% endif %}{% if v.s.updated_by.is_empty() %}never{% else %}by {{ v.s.updated_by }} {% endif %}{% if v.s.lock_id.is_empty() %}free{% else %}held by {{ v.s.locked_by }}{% if let Some(t) = v.s.locked_ms %} {% endif %}{% endif %}{{ v.versions.len() }}{% if !v.s.body.is_empty() %}download{% endif %}
{% for v in states %}{% if !v.versions.is_empty() %}
{{ v.s.name }}: {{ v.versions.len() }} kept {% if v.versions.len() == 1 %}version{% else %}versions{% endif %}

Each write keeps the state it replaced (the last {{ crate::terraform::VERSIONS_KEPT }}). Restoring one writes it again with the next serial, so OpenTofu and Terraform take it, and keeps the current state as a version: restoring never loses anything, and a later version can be restored in turn.

{% for k in v.versions %}{% endfor %}
SerialWritten byWhen
{{ k.serial }}{{ k.created_by }}download{% if repo.can_maintain() %}
{% endif %}
{% endif %}{% endfor %} {% endif %} {% endblock %}