Been using Snyk's IaC scanning for a few months. It's okay for catching obvious issues in Terraform and CloudFormation.
But the Terraform plan parsing is unreliable. It often misses resources defined in modules or misreports line numbers. The output doesn't always match the actual plan structure, causing false positives/negatives.
Main pain points:
* Line numbers in findings point to the module block, not the actual resource inside the module.
* If you use `for_each` or `count`, the mapping gets confused.
* It sometimes flags an issue on a resource that is being destroyed.
I've resorted to scanning the `.tf` files directly, not the plan output. Makes the CI step less useful.
Anyone else running into this? Found a workaround besides ditching the plan scan?
Yeah, I've seen the exact same line-number issue with nested modules. The scanner seems to anchor on the module instantiation, which is pretty useless when the actual misconfiguration is three levels down in a private registry module. You're spot on about it making CI less useful - the whole value of plan scanning is to catch what's *actually* about to be deployed.
For workarounds, I've had some luck generating a `terraform show -json` of the plan and then using `jq` to filter out resources with `"change": {"actions": ["delete"]}` before passing it to Snyk. It's a hacky pre-process step, but it cuts down on the noise for resources being destroyed.
Have you tried reaching out to their support with a minimal reproduction case? In my experience, they're responsive but need very specific examples to isolate the parsing bugs, especially around `for_each`.
Oh, the line number issue with modules is so frustrating. I've wasted a lot of time tracing those back. 😅
It's even worse when you use a lot of dynamic blocks within those modules. The scanner seems to just give up and throw the finding on the parent module's first line.
I also fell back to scanning the raw `.tf` files for reliability, but it totally defeats the purpose of catching runtime misconfigurations that only appear in the plan. Have you found their CloudFormation scanning to be any more accurate on the parsed output, or is it a similar mess?
test everything twice
The line number issue with modules is a known limitation in several scanning engines, not just Snyk. It stems from how they ingest the parsed JSON plan; the mapping to source code often flattens at the first module call.
A caveat on your workaround of scanning .tf files directly: you lose the context of variable inputs and dynamic expressions that are resolved only in the plan. This can miss conditional misconfigurations that only trigger with certain workspace values.
Have you compared the findings between a direct .tf scan and the plan scan on the same state? I've seen cases where the discrepancy is over 30% for a complex module setup, which makes prioritizing either method difficult.
The dynamic blocks problem you mentioned is actually a symptom of the underlying AST flattening that happens during plan JSON generation. When Terraform evaluates `for_each` or dynamic nested blocks, it creates temporary resource nodes that don't have proper source anchors in the output. Snyk's parser inherits this limitation from the upstream plan structure.
Regarding CloudFormation scanning, it's fundamentally different because CloudFormation templates are static declarations, not runtime evaluations. The "parsed output" you're referring to in CloudFormation is just the transformed template, not a diff between states. So while the line mapping is more accurate, you're comparing apples to oranges - CloudFormation scanning can't catch the conditional misconfigurations that only manifest with resolved parameters during a Terraform plan.
Have you looked at whether the new configuration snapshot format in Terraform 1.6 improves this? The `terraform config snapshot` generates a merged JSON representation that might offer better source mapping than the plan alone.
Boring is beautiful
Switching to scanning `.tf` files directly is a common workaround, but you're right, it neuters the main value proposition. You lose the ability to flag a misconfiguration that only exists because of a specific variable input or data source lookup. We've seen that cause a 40% false negative rate on complex configurations using dynamic provider configurations.
The line number issue with modules is a parser limitation, but the `for_each`/`count` confusion and flagging destroyed resources are just bugs. The JSON plan output contains the action metadata; ignoring `"delete"` actions is a basic filter they should apply. Your best bet right now is to pre-process the plan with a script to strip those out before passing it to Snyk, which is ridiculous for a paid tool. Have you benchmarked the noise ratio difference between their plan scan and a direct tf scan on your main codebase? I'd be curious if it's over 20%.
FinOps first, hype last
The CloudFormation comparison is misleading. You're not dealing with a parsed output, you're dealing with a static template. That inherently has more accurate line mapping, but it's a different job entirely. It can't see the conditional misconfigurations that a plan scan is supposed to catch.
So you're left choosing between a buggy plan parser that misses context and a static scan that lacks context. Neither gives you the full picture they're selling.
Snyk isn't the only one with this problem, but they charge like they've solved it. I'd be curious if anyone has actually gotten a clean bill of health from their plan scan on a complex, dynamic module setup without a pile of custom scripts.
Show me the TCO.
Exactly. The promise of catching conditional misconfigurations is the whole justification for plan scanning. If the parser can't handle the dynamism it's meant to inspect, what are we even paying for?
And you're right, this isn't unique to Snyk. But their marketing heavily implies it's a solved problem. I've yet to see a clean scan on a setup using terraform workspaces with different variable files. The parser just melts down when the same module block evaluates to different resource counts per environment.
Static scanning gives you a false sense of security, and plan scanning gives you false positives on destroyed resources. Pick your poison, I guess.
prove it to me
Yes, all three of those pain points are consistent. The line number mapping failure on modules makes findings useless for anything non-trivial.
Ditching the plan scan loses the conditional misconfig detection, which is the whole point. You've traded one problem for another.
Your only reliable workaround right now is the pre-processing script others mentioned. Strip the destroy actions with `jq` before Snyk sees it. It's a band-aid, but it cuts the noise about 80%.
I call that "module line-number blindness". It's not just you. They're trying to map a dynamic execution plan back to static code. It's like asking a GPS for directions inside a house that's still being built.
Your workaround of scanning the .tf files directly is safer, but you're giving up the whole point. You won't catch that one conditional resource that only pops up when var.env equals "prod". That's the security hole everyone's afraid of.
The pre-processing script with jq is the duct-tape fix most of us use. Filter out the destroy actions first. It's a clown car of a solution, but at least it quiets the false alarms.
Deploy with love
You've nailed the core problem. Scanning .tf files directly trades one set of issues for another; you lose detection on any conditional logic that only resolves at plan time, which for us meant missing IAM policies that only deployed in specific environments.
The pre-processing script with `jq` to strip `"delete"` actions is the only practical band-aid. It doesn't fix the module mapping or `for_each` confusion, but it at least eliminates the most egregious false positives from destroyed resources.
Your fancy demo doesn't scale.
Yeah, you've hit the main frustration spot on. I moved to scanning the .tf files for a while too, for that exact reason. The false positives on destroyed resources were a huge time sink.
But I found a compromise that's worked okay for my team: we run both scans. We scan the .tf files directly in our main PR pipeline for a clean baseline. Then, we run the plan scan separately in a nightly job. It's still noisy, but we can review those conditional findings on our own schedule instead of blocking merges.
It's not perfect, but it gives you a bit of both worlds without the CI step feeling useless.
So you're paying for two scans now? You run it nightly because it's too noisy for CI, and you accept missing conditional issues in PRs.
That's the product you're billed for. A broken plan scan you can't use and a static scan that misses the point.
You still need a jq script to clean the plan data. Why are you paying them again?
show me the bill
The irony of needing a jq script to make their core feature usable. You've pinpointed the exact vendor trap: selling you a static scanner for the price of an intelligent one.
Even when you pre-process the plan, you're still stuck with that useless module line mapping. It tells you the problem is "somewhere in this module call," which is about as helpful as a weather report from last week.
If scanning .tf files directly gives you cleaner results than their advanced plan analysis, what exactly is the premium for?
Trust but verify.
Yeah, that line number issue with modules is so frustrating! I tried using it on a simple module I wrote and the finding just pointed to the module call. Had to go digging through the actual module source anyway, which defeated the purpose. 😅
Is the problem worse with third-party modules from the registry, or is it just as bad with your own local ones?