Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,17 @@ Example `tf.env`:
AWS_DEFAULT_REGION=eu-west-1
```

### State file name

By default, the Terraform state is stored at `terraform/<repo-name>/main.tfstate` in the S3
backend bucket. If a single repo contains multiple Terraform stacks (e.g. one per subdirectory),
set `TF_STATE_FILE_NAME` per stack so each stack writes to its own state file. The variable can
be set in the environment or via `tf.env`:

```
TF_STATE_FILE_NAME=environment-reporter.tfstate
```

## Development

### Prerequisites
Expand Down
10 changes: 7 additions & 3 deletions bin/tf
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,12 @@ class TfVarsFiles:


class TfBackend:
def __init__(self, account_id: str, region: str, repo_name: str):
def __init__(self, account_id: str, region: str, repo_name: str,
state_file_name: str = "main.tfstate"):
self._account_id = account_id
self._region = region
self._repo_name = repo_name
self._state_file_name = state_file_name

@property
def bucket(self) -> str:
Expand All @@ -114,7 +116,7 @@ class TfBackend:

@property
def state_path(self) -> str:
return f"terraform/{self._repo_name}/main.tfstate"
return f"terraform/{self._repo_name}/{self._state_file_name}"

def init_command(self, extra_args: list[str] | None = None) -> list[str]:
command = [
Expand Down Expand Up @@ -198,8 +200,10 @@ class TfRunner:
if not account_id:
raise TfError("Cannot determine AWS account ID for backend config.")
repo_name = TfBackend._get_repo_name()
state_file_name = os.environ.get("TF_STATE_FILE_NAME", "main.tfstate")
return TfBackend(
account_id=account_id, region=context.region, repo_name=repo_name
account_id=account_id, region=context.region, repo_name=repo_name,
state_file_name=state_file_name,
)

def _run_init(self, backend: TfBackend) -> None:
Expand Down
17 changes: 17 additions & 0 deletions tests/test_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,23 @@ def test_state_path_varies_by_repo(self):

assert backend_a.state_path != backend_b.state_path

def test_state_path_uses_custom_state_file_name(self):
backend = tf.TfBackend(account_id="123456789012", region="eu-west-1",
repo_name="my-repo",
state_file_name="environment-reporter.tfstate")

assert backend.state_path == \
"terraform/my-repo/environment-reporter.tfstate"

def test_state_path_defaults_to_main_tfstate(self):
backend_default = tf.TfBackend(account_id="123456789012",
region="eu-west-1", repo_name="my-repo")
backend_explicit = tf.TfBackend(account_id="123456789012",
region="eu-west-1", repo_name="my-repo",
state_file_name="main.tfstate")

assert backend_default.state_path == backend_explicit.state_path


class TestTfBackendDataDir:
def test_data_dir_includes_environment_id(self):
Expand Down
27 changes: 27 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,33 @@ def test_init_preserves_extra_args(self, monkeypatch):
command = mock_execvp.call_args[0][1]
assert "-upgrade" in command

def test_init_uses_tf_state_file_name_env_var(self, monkeypatch):
monkeypatch.setenv("AWS_VAULT", "staging")
monkeypatch.setenv("AWS_DEFAULT_REGION", "eu-west-1")
monkeypatch.setenv("TF_STATE_FILE_NAME", "environment-reporter.tfstate")

with patch("tf.TfBackend._get_repo_name", return_value="my-repo"), \
patch("tf.TfVarsFiles._get_account_id", return_value="123456789012"), \
patch("tf.os.execvp") as mock_execvp:
tf.TfRunner(["init"]).call()

command = mock_execvp.call_args[0][1]
assert "-backend-config=key=terraform/my-repo/" \
"environment-reporter.tfstate" in command

def test_init_defaults_to_main_tfstate_when_env_var_unset(self, monkeypatch):
monkeypatch.setenv("AWS_VAULT", "staging")
monkeypatch.setenv("AWS_DEFAULT_REGION", "eu-west-1")
monkeypatch.delenv("TF_STATE_FILE_NAME", raising=False)

with patch("tf.TfBackend._get_repo_name", return_value="my-repo"), \
patch("tf.TfVarsFiles._get_account_id", return_value="123456789012"), \
patch("tf.os.execvp") as mock_execvp:
tf.TfRunner(["init"]).call()

command = mock_execvp.call_args[0][1]
assert "-backend-config=key=terraform/my-repo/main.tfstate" in command


class TestTfRunnerAutoInit:
def test_plan_runs_init_before_command(self, monkeypatch):
Expand Down