Module 5: Securing The Ai Workload

5. Hands-on: Trivy on the new Terraform

Description

This lesson runs trivy config for real — 0.74.0, the same version cloud-security-and-guardrails-guide, Module 5, already pinned, with nothing reinstalled — over this guide's two genuinely new pieces of Terraform: bedrock.tf and modules/bedrock-guardrail/. The result, exactly as it ran while writing this lesson, is honest in both directions: no false promise that Trivy "certifies" the guardrail is correctly configured from a business standpoint, and no simulation of findings that didn't occur.

Connection to the module

cloud-security-and-guardrails-guide, Module 5, lesson 4, ran trivy config . over that guide's complete project and found eleven real findings, spread across four files — dynamodb.tf, lambda.tf, modules/s3-bucket/main.tf, secrets.tf —, while iam.tf/oidc.tf, already hardened by that guide's Module 2, produced no findings at all. This lesson repeats exactly the same command — same binary, same version, zero reinstallation — over the Terraform this module adds, and documents with the same honesty what it found.


Analogy revisited: the same inspector, now in the building's new wing

cloud-security-and-guardrails-guide, Module 5, lesson 4, compared Trivy to an inspector who walks the whole house with a two-hundred-point checklist. This lesson is that same inspector, entering for the first time the wing of the building this module just built — bedrock.tf, modules/bedrock-guardrail/. It doesn't bring a different list, it doesn't need anyone to explain what a Bedrock guardrail is before starting: it applies exactly the same list as always, and reports, with the same precision, what it finds — or doesn't find.


Step 1 — The command, on exactly what's new

Unlike trivy config . (which scans the entire andes-cargo-infra/ project, inherited and new mixed together), this lesson isolates this module's genuinely new Terraform, so the result isn't confused with findings that already existed before this module:

trivy config bedrock.tf modules/bedrock-guardrail/

What to expect (the first attempt, literal — Trivy doesn't accept more than one DIR as a positional argument):

FATAL	Fatal error	multiple targets cannot be specified

A real error, and it's worth reading it carefully instead of dismissing it: trivy config accepts exactly one DIR as a target, not a list of loose files and folders. The correct way to isolate "only what's new in this module" is to scan a directory that contains exactly those files — not pass it two different paths in the same invocation.


Step 2 — The correct command: an isolated directory with just what's new

mkdir -p bedrock-scan/modules
cp bedrock.tf locals.tf variables.tf bedrock-scan/
cp -r modules/bedrock-guardrail modules/iam-role bedrock-scan/modules/
cd bedrock-scan
trivy config .

modules/iam-role/ gets included too, even though it isn't new to this module — bedrock.tf references it with source = "./modules/iam-role" to declare BedrockManifestExtractorRole — because without it Trivy couldn't resolve the complete module and would report a reference error, not a real finding.

What to expect (literal — executed to write this lesson, with Trivy 0.74.0):

2026-08-14T15:49:53-06:00	INFO	[misconfig] Misconfiguration scanning is enabled
2026-08-14T15:49:53-06:00	INFO	[checks-client] Using existing checks from cache	path="~/Library/Caches/trivy/policy/content"
2026-08-14T15:49:53-06:00	INFO	[terraform scanner] Scanning root module	file_path="."
2026-08-14T15:49:53-06:00	INFO	Detected config files	num=1

Report Summary

┌────────┬───────────┬───────────────────┐
│ Target │   Type    │ Misconfigurations │
├────────┼───────────┼───────────────────┤
│ .      │ terraform │         0         │
└────────┴───────────┴───────────────────┘
Legend:
- '-': Not scanned
- '0': Clean (no security findings detected)

Zero findings. Before interpreting that 0 — and it's easy to misread it in either direction — confirm with --debug that Trivy really did read all nine files involved, not that it silently skipped something:

trivy config . --debug 2>&1 | grep -i "parsing\|module"

What to expect (trimmed to the relevant lines, literal):

DEBUG	[terraform parser] Parsing	module="root" file_path="bedrock.tf"
DEBUG	[terraform parser] Added file	module="root" file_path="bedrock.tf"
DEBUG	[terraform parser] Parsing	module="root" file_path="locals.tf"
DEBUG	[terraform parser] Added file	module="root" file_path="locals.tf"
DEBUG	[terraform parser] Parsing	module="root" file_path="variables.tf"
DEBUG	[terraform parser] Added file	module="root" file_path="variables.tf"
DEBUG	[terraform parser] Parsing FS	module="root" file_path="modules/bedrock-guardrail"
DEBUG	[terraform parser] Parsing	module="root" file_path="modules/bedrock-guardrail/main.tf"
DEBUG	[terraform parser] Added file	module="root" file_path="modules/bedrock-guardrail/main.tf"
DEBUG	[terraform parser] Parsing FS	module="root" file_path="modules/iam-role"
DEBUG	[terraform parser] Parsing	module="root" file_path="modules/iam-role/main.tf"
DEBUG	[terraform parser] Added file	module="root" file_path="modules/iam-role/main.tf"

Nine files, all nine parsed with no syntax or reference error — the report's 0 is a real result of having evaluated the complete HCL, not the result of Trivy giving up before reading it.


Why the result is zero, precisely

It's worth being exact about what this result means — and what it does NOT mean —, with the same honesty discipline as the rest of this guide. Trivy's checks bundle — the same rule set that found eleven real findings against s3.tf, dynamodb.tf, lambda.tf, and secrets.tf in cloud-security-and-guardrails-guidedoesn't yet contain any rule specific to aws_bedrock_guardrail. aws_bedrock_guardrail is a relatively new resource in the hashicorp/aws provider (this guide's Module 3, lesson 1 already confirmed version v6.60.0 in this environment); Trivy's rules for a specific resource get written and added to the bundle over time, as the security community develops them — at the time this lesson was written, that work doesn't exist yet for this particular resource.

BedrockManifestExtractorRole (via modules/iam-role/), on the other hand, is subject to real Trivy rules about IAM policies — and it passes clean, not because rules are absent, but because Module 3, lesson 4's least privilege (one action, one resource, no wildcard) doesn't fire any of them. This is, in practice, the same pattern cloud-security-and-guardrails-guide, Module 5, lesson 4 already documented for iam.tf/oidc.tf: a file can be absent from the findings table for two completely different reasons — because no one has written a matching rule yet (the guardrail's case), or because the HCL already complies with rules that do exist (the role's case).

   WHY "0 FINDINGS" DOESN'T MEAN THE SAME THING FOR EACH PIECE

   modules/bedrock-guardrail/          modules/iam-role/ (BedrockManifestExtractorRole)
   │                                    │
   │  0 findings because Trivy's       │  0 findings because the HCL DOES comply
   │  *checks bundle* still has        │  with real rules that DO exist
   │  no rules for this resource       │  (IAM least privilege, already
   │  type                              │  verified too by
   │                                    │  bedrock-least-privilege.rego, L3)
   ▼                                    ▼
   "no coverage yet",                  "real coverage, passed clean"
   not "certified secure"

This is exactly the same kind of limit this module's lesson 4 already named for Bedrock's API keys: precision about what IS verified versus what falls outside the tool's scope, without inflating the result in either direction.


Step 3 — Confirming the rest of the project is still clean

To close the loop, confirm that adding bedrock.tf introduced no new findings in the rest of andes-cargo-infra/ — the same additive argument this guide's Module 3, lesson 5 already made with terraform plan (17 to add, 0 to change, never a broken 0 to change):

cd ..
trivy config . --exit-code 1 --severity CRITICAL,HIGH
echo "exit: $?"

What to expect (literal — over the complete andes-cargo-infra/ project, already hardened by cloud-security-and-guardrails-guide up to its capstone; the exact same command the iac-scan job in ci.yml runs):

Report Summary

┌────────┬───────────┬───────────────────┐
│ Target │   Type    │ Misconfigurations │
├────────┼───────────┼───────────────────┤
│ .      │ terraform │         0         │
└────────┴───────────┴───────────────────┘
Legend:
- '-': Not scanned
- '0': Clean (no security findings detected)
exit: 0

Zero CRITICAL/HIGH findings across the whole project, bedrock.tf included — the same severity filter ci.yml (inherited from cloud-security-and-guardrails-guide, Module 8, lesson 3) already uses in the iac-scan job. This module's lesson 8 runs this exact command inside that real job, with act pull_request.


Common mistakes

Passing several loose files/folders to trivy config, expecting it to work like cp or git add (assuming every CLI command accepts multiple paths). What happens: someone, used to shell commands that accept a list of paths, writes trivy config bedrock.tf modules/bedrock-guardrail/ directly. How to spot it: the multiple targets cannot be specified message, shown as-is in this lesson's Step 1. How to fix it: trivy config scans exactly one DIR — to isolate a subset of files, copy them into a dedicated directory (Step 2), or scan the entire project and filter the result by file in the Report Summary table.

Interpreting "0 findings" in modules/bedrock-guardrail/ as "Trivy certified the guardrail is correctly configured" (over-trusting the absence of a finding). What happens: someone reads 0 in the table and concludes the guardrail's six policies (Module 4) are correctly configured from a security standpoint, with the same confidence they'd have in a real Trivy finding. How to spot it: if your explanation of this result doesn't mention that Trivy's checks bundle still has no specific rules for aws_bedrock_guardrail. How to fix it: this lesson's "Why the result is zero, precisely" section exists exactly for this distinction — a 0 can mean "no problems found" or "no rules to look for problems yet," and confusing the two cases is exactly the kind of false sense of security cloud-security-and-guardrails-guide, Module 1, lesson 2 already warned about with "green isn't synonymous with secure."

Forgetting that modules/iam-role/ has to be copied along with modules/bedrock-guardrail/ for the isolated scan to work (a cross-module dependency, specific to this project). What happens: someone copies only bedrock.tf and modules/bedrock-guardrail/ into the isolated directory, without modules/iam-role/, and Trivy can't resolve the source = "./modules/iam-role" reference bedrock.tf declares for BedrockManifestExtractorRole. How to spot it: a Trivy message about a module not found, or a Report Summary that only covers the guardrail and silently omits the role. How to fix it: this lesson's Step 2 copies all three necessary directories (bedrock.tf, modules/bedrock-guardrail/, modules/iam-role/) — any HCL isolation for scanning has to include everything that HCL references, not just the file that at first glance looks like "the new one."


Exercises

Exercise 1 — Run trivy config . over the complete andes-cargo-infra/ (without the --severity filter), and compare the result with cloud-security-and-guardrails-guide, Module 5, lesson 4. Would you expect more, fewer, or the same eleven findings that lesson documented?

See solution

Fewer — zero, in fact, if you run it against the project as this guide inherits it: cloud-security-and-guardrails-guide found those eleven findings in its Module 5, but resolved them over the course of its own Module 5 (lesson 6: "fix, suppress, or accept") before reaching its capstone (Module 8). The project this guide inherits is the state after those fixes — the same "inherits without repeating" argument this guide's Module 1 already established for all the prior infrastructure.

Exercise 2 — Explain why trivy config bedrock-scan/ (the isolated directory) and trivy config . (the complete project, filtered to just bedrock.tf's lines) could, in theory, give different results for the same file. Think about what information Trivy has available in each case.

See solution

They could differ if bedrock.tf depended on some value defined in another file in the complete project that isn't present in the isolated directory — for example, if a variable with an insecure default were declared in the root project's variables.tf but the isolated directory had a different version of that file. In this specific lesson that doesn't happen, because Step 2 copied all three relevant root files (bedrock.tf, locals.tf, variables.tf) exactly as they exist in the complete project — but in general, any isolated scan of a subset of HCL runs the risk of losing context the complete project does have, the same reason Step 3 of this lesson also confirms the result against the entire project.

Exercise 3 — Predict what would happen to this lesson's Report Summary if, in the future, Trivy's security community added a new rule specific to aws_bedrock_guardrail — for example, requiring content_policy_config to always be present. Would this guide's HCL pass or fail that hypothetical rule?

See solution

It would pass: bedrock.tf (this guide's Module 4) declares content_policy_config with an active PROMPT_ATTACK filter, along with the guardrail's other five complete policies — so a hypothetical rule requiring that specific policy's presence would find the HCL already complies. The point of this exercise is to notice that "0 findings today" and "0 findings tomorrow, once more rules exist" aren't guaranteed to be the same claim — Trivy's result is a snapshot of the checks bundle at the exact moment of the run, not a permanent promise, the same temporal-honesty discipline this guide applies to any executed result.


Summary and next step

This lesson ran trivy config for real, with 0.74.0, the same version inherited without reinstalling, over bedrock.tf and modules/bedrock-guardrail/ (plus modules/iam-role/, needed to resolve the reference). The result — 0 misconfigurations — was confirmed with --debug as a real result, from nine files parsed with no error, not a silently incomplete scan. You precisely distinguished why that 0 means different things for the guardrail (no specific rules yet in the checks bundle) and for the IAM role (real rules, passed clean) — and confirmed, against the complete project with the same severity filter ci.yml uses, that adding this module introduced no new CRITICAL/HIGH findings.

Before moving on you should be able to: explain why trivy config rejects multiple positional paths; distinguish "no findings due to a lack of rules" from "no findings because the HCL complies with real rules"; and run the exact same command the iac-scan job in ci.yml uses, from your own terminal.

Lesson 6 signs, with cosign, this guide's first genuinely new binary artifact: extract-shipment-manifest-fields's .zip.

Resources

  1. trivy.dev — Terraform coverage — official documentation for Trivy's Terraform coverage, including the list of resources with active rules.
  2. Terraform Registry — aws_bedrock_guardrail — the resource that, at the time this lesson was written, still has no dedicated rules in Trivy's checks bundle.
  3. cloud-security-and-guardrails-guide, Module 5, lesson 4 (04-hands-on-scanning-andes-cargo-infra-with-trivy.md) — the original trivy config . run that found the eleven findings already resolved, inherited by this guide without repeating.
  4. cloud-security-and-guardrails-guide, Module 8, lesson 3 — the iac-scan job in ci.yml, with the exact command (trivy config --exit-code 1 --severity CRITICAL,HIGH .) this lesson reproduced in Step 3.