{% 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 %}
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"
}
}
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).
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.
{{ 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.
| State | Serial | Last written | Lock | Versions 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 %} |
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.
| Serial | Written by | When | |
|---|---|---|---|
| {{ k.serial }} | {{ k.created_by }} | download{% if repo.can_maintain() %} {% endif %} |