assert-opentofu-resource-is-used
Assert that every OpenTofu resource in a subsystem is reached by something.
Why
A resource nothing references still deploys. It is planned, applied and
paid for on every deployment, it never does anything, and no tool in a
normal pipeline objects: tofu validate and tflint accept it, because
a resource with no dependents is legal OpenTofu and is frequently the
point of one. A DNS record and an email identity are reached by what
they publish rather than by an address.
So the question this asks is not "does anything reference it" but "is it reached at all", and a resource is credited by four different kinds of reach. Only a resource that fails all four is refused.
Installation
pip install assert-opentofu-resource-is-used
Usage
# One subsystem, crediting a use from anywhere in the repository
assert-opentofu-resource-is-used src/api/common/routing --search-in .
# Several subsystems at once
assert-opentofu-resource-is-used src/www/common src/bootstrap --search-in .
Options
| Option | Effect |
|---|---|
--search-in PATH |
A tree to credit a use from, repeatable. |
--exclude PATTERNS |
Comma-separated globs to leave out. |
--annotate |
Print each finding as a GitHub Actions ::error annotation. |
--quiet |
Print nothing; report through the exit code. |
--count |
Print only how many findings there were. |
--verbose |
Print the files read, the findings and a summary. |
--fail-fast |
Stop at the first finding. |
--warn-only |
Always exit 0. |
Exit codes
| Code | Meaning |
|---|---|
| 0 | Every resource is reached |
| 1 | A resource is reached by nothing |
| 2 | A directory was missing or a file was unreadable |
What is read
Each directory argument is read for the *.tf files directly in it, and
nothing below it. A module directory beneath a subsystem is a namespace
of its own: nothing in the parent can interpolate an address inside it,
so pooling the two would credit a resource with a use that OpenTofu
could not resolve. Point the tool at a module directory to check the
module.
Every tree given to --search-in is walked for .tf, .py, .yml,
.sh, .js and .ts files, which are where the four credits below
live. Pass the whole repository: a remote state reader lives in another
subsystem, a terraform_output call under the tests, and a
terraform output -raw step in a workflow file.
What counts as a use
A resource is refused only when all four of these hold at once.
Nothing interpolates its address
aws_dynamodb_table.events written anywhere else in the directory is a
use, whether inside a string interpolation or in a bare expression.
An interpolation inside an output block is the exception, and it is
the trap this tool is built around. Exporting a resource is not using
it, so an interpolation there counts only when something reads that
output, and three things read one:
| Reader | Shape |
|---|---|
| Another subsystem's OpenTofu | data.terraform_remote_state.x.outputs.name |
| A test or a library, in Python | terraform_output(directory, "name") |
| A workflow step | terraform output -raw name |
A resource interpolated only by outputs nothing reads is refused along with them.
Its own body interpolates nothing of the directory
A resource attached at one end is attached. aws_lambda_permission
names the function it permits and aws_iam_role_policy names the role
it attaches to, and neither is named by anything in turn. So a body that
interpolates another resource, a data source or a module of the same
directory is a use, and only a body naming nothing but local, var
and literals can be refused.
No string it assigns is spelled again
This is attachment by value rather than by address. An SES identity
declared as email = "contact@${local.domain_name}" is what makes a
policy condition and an environment variable spelling that same address
work, and nothing interpolates the identity. So every string a top-level
attribute of the block is assigned is looked for verbatim elsewhere in
the directory's .tf files, and finding one is a use.
No program names its identifier
A resource can be reached from outside OpenTofu entirely, an SSM
parameter read at runtime by name being the usual case. The literal
parts of the identity attribute, split on the ${...} interpolations,
are looked for in every .py, .yml, .sh, .js and .ts file in
the searched trees, and finding one is a use.
The identity attributes are a closed list: alarm_name, alias,
bucket, domain_name, email, function_name, group_name,
identifier, key, log_group_name, name, parameter_name,
policy_name, queue_name, repository_name, role_name,
rule_name, stage_name, statement_id, table_name, topic_name,
trail_name, user_name and vault_name. A resource type that spells
its identifier some other way is refused rather than credited, which is
a red run answered by adding the spelling.
GitHub Actions
- name: Assert every OpenTofu resource is used
uses: 10U-Labs/assert-opentofu-resource-is-used@latest
with:
directories: src/api/common/routing
search-in: .
annotate defaults to true there, so each finding lands on the line it
names in the diff.
License
Apache-2.0
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file assert_opentofu_resource_is_used-20260828010626.tar.gz.
File metadata
- Download URL: assert_opentofu_resource_is_used-20260828010626.tar.gz
- Upload date:
- Size: 34.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f403006066342c73db44fd33a11eabb02c64d2924b7331b30d174bf610403399
|
|
| MD5 |
71d6ca056e094df1468aff2ef5d4dfe4
|
|
| BLAKE2b-256 |
a7fb843f2f5742bebc74e3388a13845fc5a4d4e4d342964e377f2b4a0d3f629c
|
Provenance
The following attestation bundles were made for assert_opentofu_resource_is_used-20260828010626.tar.gz:
Publisher:
release.yml on 10U-Labs/assert-opentofu-resource-is-used
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
assert_opentofu_resource_is_used-20260828010626.tar.gz -
Subject digest:
f403006066342c73db44fd33a11eabb02c64d2924b7331b30d174bf610403399 - Sigstore transparency entry: 2620896426
- Sigstore integration time:
-
Permalink:
10U-Labs/assert-opentofu-resource-is-used@ae3e296379c338de12022d075a8fcef7f515be01 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/10U-Labs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ae3e296379c338de12022d075a8fcef7f515be01 -
Trigger Event:
push
-
Statement type:
File details
Details for the file assert_opentofu_resource_is_used-20260828010626-py3-none-any.whl.
File metadata
- Download URL: assert_opentofu_resource_is_used-20260828010626-py3-none-any.whl
- Upload date:
- Size: 18.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
80ddb16c97c3cabc2d7185554d833231a4b7291a7bd76629b609772ff8a31fab
|
|
| MD5 |
e07dd22507525fb9f21219b6bbd1df09
|
|
| BLAKE2b-256 |
594302da0dc67bff07aee42acafbc95112fec8004ec5080e135e67d5b4c73af1
|
Provenance
The following attestation bundles were made for assert_opentofu_resource_is_used-20260828010626-py3-none-any.whl:
Publisher:
release.yml on 10U-Labs/assert-opentofu-resource-is-used
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
assert_opentofu_resource_is_used-20260828010626-py3-none-any.whl -
Subject digest:
80ddb16c97c3cabc2d7185554d833231a4b7291a7bd76629b609772ff8a31fab - Sigstore transparency entry: 2620896435
- Sigstore integration time:
-
Permalink:
10U-Labs/assert-opentofu-resource-is-used@ae3e296379c338de12022d075a8fcef7f515be01 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/10U-Labs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ae3e296379c338de12022d075a8fcef7f515be01 -
Trigger Event:
push
-
Statement type: