The ts-obom scanner extracts an Operations Bill of Materials (OBOM) from infrastructure-as-code: the resources a module is deployed as -- functions, tables, buckets, queues, task definitions -- together with the IAM grants that say which of them may do what to which other. It reads CloudFormation, AWS SAM, Terraform and OpenTofu sources and writes a CycloneDX document that the TrustSource platform stores. It is the infrastructure counterpart to ts-scan, which produces the Software Bill of Materials (SBOM) of the code.
Where an SBOM answers what is in the software, an OBOM answers what the software is operated as, and what it is allowed to touch once it runs. That is the evidence trust-boundary and threat-modelling work needs and which SBOM signals alone cannot provide.
ts-obom scans a directory for IaC sources and emits, per scanned directory, a document with
resources- every resource declared in the sources, with its type and the file it comes from. In the CycloneDX output these are the componentsedges- access grants{principal, resource, actions[], effect, grantedVia}, for example the role of a Lambda function that mays3:GetObjecton a bucket ARN, with the policy or SAM policy template that granted it. In the CycloneDX output these are component properties anddependenciesedgesunresolved- grants the scanner saw but could not expand, for example managed policy ARNs that would need AWS API access to resolve
Supported IaC front-ends:
| Front-end | Sources | What is extracted |
|---|---|---|
cloudformation |
CloudFormation and AWS SAM templates (.yaml, .yml, .json) |
AWS::IAM::Role inline policies, AWS::Serverless::Function policies including the common SAM policy templates (DynamoDBCrudPolicy, S3ReadPolicy, ...) |
terraform |
Terraform and OpenTofu (.tf, .tf.json, .tofu, .tofu.json; a .tofu file shadows a .tf file of the same name, as in OpenTofu) |
aws_iam_role_policy / aws_iam_user_policy / aws_iam_group_policy inline documents, *_policy_attachment resources, resolved to the compute resource assuming the role where possible |
The graph is built with a trimmed, vendored subset of Checkov's resource graph builder; see src/ts_obom/_vendor/NOTICE.md for provenance and changes. No Checkov installation and no cloud credentials are required; the scan is fully offline.
pip install ts-obomgit clone https://github.com/trustsource/ts-obom.git
cd ts-obom
pip install .docker pull trustsource/ts-obomScan a local checkout by mounting it into the container:
docker run --rm -v "$(pwd)":/workspace trustsource/ts-obom scan -o /workspace/obom.json /workspace/infrastructureThe command set follows the conventions of ts-scan: verbs as sub-commands, -o/--output and -f/--format for results, --<front-end>:<option> for front-end specific switches, a profile-based config file and an optional tsproject.toml in the scanned directory.
ts-obom scan -o <path to the output file> [-f <output format>] <path to one or more directories>The -f <output format> option controls the output format and can be:
ts- the scanner's own JSON format (default), one document per scanned directorycyclonedx- a CycloneDX 1.6 Operations BOM, the format the TrustSource platform storesdot- a Graphviz digraph of the access graph, for examplets-obom scan -f dot infra | dot -Tsvg -o obom.svg
Which sources are read:
--cloudformation:ignore- Skip CloudFormation and SAM templates--terraform:ignore- Skip Terraform and OpenTofu sources
Which deployment the result describes — see Deployments below:
--deployment <NAME>- Names the deployment: an environment (DEV,PRD) or a customer setup (kunde1)--cloudformation:parameters <FILE>- Applies a CloudFormation parameter file on top of the templates' declared defaults. Reads[{"ParameterKey": ..., "ParameterValue": ...}]— the formatsam deploy --parameter-overridesis fed from — or a flat name/value mapping--terraform:var-file <FILE>- Applies a.tfvarsfile on top of the variable defaults, asterraform -var-filewould
Recorded alongside the result:
--tag <TAG>- Stores the SCM tag<TAG>in the result--branch <BRANCH>- Stores the SCM branch<BRANCH>in the result--verbose- Enables verbose mode
The full list of options can be printed using:
ts-obom scan --helpOne project is usually deployed several times, and the interesting questions are comparative: what does production have that development does not, what does one customer setup grant that another does not.
ts-obom scan -f cyclonedx --deployment PRD \
--cloudformation:parameters params4PRD.json -o obom-prd.cdx.json .A name on its own is not enough. The scan resolves Ref, !Sub and Terraform variables against the defaults declared in the sources. In the usual pattern — one template, one parameter file per environment — the entire difference between two deployments lives in those parameter files, so without them two scans of the same sources produce identical documents. Naming one DEV and the other PRD would then show no difference, which says nothing about the deployments and everything about how the documents were made.
Every result therefore records where its parameter values came from, and this field is never omitted:
parameterSource |
Meaning |
|---|---|
defaults |
Nothing was supplied. Two documents that both say this are not comparable, however they are named |
cloudformation=params4PRD.json |
Values came from that file |
cloudformation=defaults,terraform=params4PRD.tfvars |
Half parameterised — Terraform got values, CloudFormation did not |
Naming a deployment without supplying values is allowed, and warned about — once when the scan produces such a document, and again before it is uploaded. A value supplied for a parameter that no scanned template declares is reported under unresolved as unused-parameter, rather than silently doing nothing.
Scanning a directory with a SAM template
ts-obom scan -o obom.json ./backendproduces a document like
[
{
"module": "backend",
"moduleId": "obom:backend",
"source": "/work/orderdesk/backend",
"deployment": "PRD",
"parameterSource": "cloudformation=params4PRD.json",
"tool": { "name": "ts-obom", "version": "0.4.0", "frontends": ["cloudformation", "terraform"], "generatedAt": "2026-09-22T12:00:00+00:00" },
"edges": [
{
"principal": "AWS::Serverless::Function.OrderApiFunction",
"resource": "Ref:OrdersTable",
"actions": ["dynamodb:GetItem", "dynamodb:DeleteItem", "dynamodb:PutItem", "dynamodb:Scan", "dynamodb:Query", "dynamodb:UpdateItem", "dynamodb:BatchWriteItem", "dynamodb:BatchGetItem", "dynamodb:DescribeTable", "dynamodb:ConditionCheckItem"],
"effect": "Allow",
"grantedVia": "SAM policy template 'DynamoDBCrudPolicy'"
}
],
"unresolved": []
}
]The exact principal, resource and grantedVia strings depend on the front-end; the format documentation describes them.
The platform stores OBOMs as CycloneDX, so a transfer is a scan in that format followed by an upload:
ts-obom scan -f cyclonedx -o obom.cdx.json .
ts-obom upload --api-key "$TS_API_KEY" --project-name Orderdesk obom.cdx.jsonScan the project root and upload at project scope: the front-ends recurse, so one run covers the whole project, and that is where an OBOM belongs. A module is something that produces a deployment artefact, which infrastructure does not divide into -- a queue or a table belongs to no artefact. --module-name exists for the case where the scanned sources really are one module's own infrastructure. The obom feature has to be enabled for the company; the key travels as the x-api-key header, and --base-url carries the API version (default https://api.trustsource.io/v2).
Uploading a file written with -f ts is refused locally -- the API would only reject it -- with a pointer to -f cyclonedx.
Like ts-scan, ts-obom reads defaults from a TOML config file with profiles. The default location is ~/.ts-obom/config; it is created on first run:
[default]
format = "ts"
[ci]
format = "ts"
verbose = trueSelect a profile with ts-obom -p ci scan ... and a different file with ts-obom --config <path> scan .... Options can also be set through environment variables prefixed with TS_OBOM_, for example TS_OBOM_SCAN_OUTPUT_PATH. A tsproject.toml in a scanned directory provides per-project defaults.
ts-scan and ts-obom are deliberately separate tools. Software composition (SBOM) and operational deployment (OBOM) are different questions with different consumers, and mixing them into one command set would blur what each result means. Both share the same conventions, packaging and release process, and the OBOM document header (module, moduleId, source, tag, branch) mirrors ts-scan's scan header so that results can be correlated per module.
ts-obom is licensed under the Apache License 2.0. The vendored Checkov subset under src/ts_obom/_vendor/checkov is licensed under the Apache License 2.0 by its respective copyright holders; see the LICENSE and NOTICE.md files in that directory.