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
Metadata
Release files for assert-opentofu-resource-is-used 20260828010626
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| assert_opentofu_resource_is_used-20260828010626.tar.gz | 34.5 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| assert_opentofu_resource_is_used-20260828010626-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 53.1 kB
Release files / assert_opentofu_resource_is_used-20260828010626.tar.gz
| Download URL | assert_opentofu_resource_is_used-20260828010626.tar.gz |
|---|---|
| Size | 34.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f403006066342c73db44fd33a11eabb02c64d2924b7331b30d174bf610403399
|
|
BLAKE2b-256 checksum How to use checksums |
a7fb843f2f5742bebc74e3388a13845fc5a4d4e4d342964e377f2b4a0d3f629c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 28, 2026.
Transparency logRelease files / assert_opentofu_resource_is_used-20260828010626-py3-none-any.whl
| Download URL | assert_opentofu_resource_is_used-20260828010626-py3-none-any.whl |
|---|---|
| Size | 18.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
80ddb16c97c3cabc2d7185554d833231a4b7291a7bd76629b609772ff8a31fab
|
|
BLAKE2b-256 checksum How to use checksums |
594302da0dc67bff07aee42acafbc95112fec8004ec5080e135e67d5b4c73af1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 28, 2026.
Transparency logSecurity for assert-opentofu-resource-is-used 20260828010626
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for assert_opentofu_resource_is_used-20260828010626.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 28, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.