The scenario is invented, but it is assembled from familiar details. A platform team maintains an internal object-storage module that some thirty services depend on. To make life easier for one team that needs public access to static assets, someone adds a public_read variable to the module. Nobody remembers to set its default to false, and the reviewer looks only at the changed lines without noticing that the default has become permissive. The pipeline scanner fires, but that rule was moved to advisory mode long ago because it was "too noisy." A week later the module is updated across all consumers, and some buckets holding exports and backups become readable from outside. Nobody broke through a perimeter: the infrastructure did exactly what the code described.
There is no single "vulnerability" in this story that a port scanner would find. There is a chain of decisions: how the shared module is built, what its defaults are, who approves changes and how, what the pipeline does with policy warnings, and how quickly a module fix propagates across environments. That chain is exactly what an infrastructure as code security assessment should examine. Below we cover what such an audit includes, how to adapt general testing methodology to it, where automation stops helping, and how to commission work with reproducible, comparable results.
How IaC Assessment Differs from Testing Running Servers
A traditional technical assessment walks through live systems, host by host and service by service. An infrastructure as code assessment looks at the source those hosts come from: Terraform or OpenTofu templates, CloudFormation, manifests and Helm charts, reusable modules, variable files, CI/CD pipeline definitions and policy-as-code rules. It also looks at the delivery process that turns that code into networks, accounts, storage and load balancers.
That gives the audit a two-part question. First, does the described infrastructure deliver the security properties the organization treats as mandatory, such as storage that is closed by default, narrow roles, logging that is switched on, and encryption? Second, does the pipeline stop an unsafe change before it reaches production, or does it merely mention it in a log nobody reads?
IaC has a property that changes the economics of testing. Decisions are centralized: one mistake in a popular module is repeated in every environment that consumes it. The same property also scales fixes. A corrected module default helps remove the shared cause. For each consumer, verify that the update was applied, unsafe overrides were removed and the actual resources reached the intended state. This is an observation about the nature of IaC, not a statistic, but it determines where to dig deeper and how to group findings.
It is equally important not to overstate what code analysis proves. A static review of a repository shows what is described. It does not prove that the live environment matches the description: resources may have been changed by hand in the console, some controls are inherited from organization- or account-level policies, and some values only appear at apply time. A mature assessment therefore always states the level at which each piece of evidence was obtained: code, change plan, test environment or, under separate authorization, the deployed environment.
Audit Scope: What Is In and What Needs Separate Authorization
The scope of an IaC assessment is broader than "the templates folder." It includes declarative templates and reusable modules, including internal module catalogs; variable files, per-environment overlays and parameter sources; pipeline definitions that plan, approve and apply changes; policy-as-code rules together with the register of approved exceptions; and the handling of state files, secrets and pipeline logs. Edge configuration defined by templates deserves its own mention: HTTP response headers on gateways, load balancers and ingress controllers, such as Content-Security-Policy, CORS rules and Permissions-Policy. When those headers are described in code, they become a testable object on a par with network access rules.
Just as important is what is not in scope without explicit written authorization: any action against production systems, changes to shared accounts, subscriptions and tenants, and testing of third-party services and providers. Authorization and rules of engagement are agreed before the technical work starts, not along the way. They should state plainly which environments may be deployed and tested, which credentials are issued, who can halt the work, and how discovered secrets are to be handled.
A separate point is the data the assessor will see. State files and plan output can contain internal resource addresses, identifiers and sometimes passwords and keys. Where that data is kept during the project and how it is deleted afterwards is part of scope, not a formality.
If your IaC mostly describes cluster infrastructure, some questions overlap with a Kubernetes security audit: there, manifests, RBAC and network policies are examined as objects of a running cluster; here, they are examined as the code the cluster is built from.
Method: Applying NIST SP 800-115 to Templates and Pipelines
None of the references used here supplies a complete IaC assessment method on its own. A convenient starting point is NIST SP 800-115, the Technical Guide to Information Security Testing and Assessment, published in September 2008 to replace SP 800-42. It describes how to plan and conduct technical tests, analyze findings and develop mitigation strategies, and it names two purposes for such testing: finding vulnerabilities and verifying compliance with a policy or other requirements. Both matter for IaC, and, as we will see, it pays not to mix them.
It is an overview of techniques rather than a comprehensive program, and it does not discuss IaC, so its logic has to be translated rather than copied.
Planning, Execution, Post-Execution
Formally, SP 800-115 divides an assessment into three phases. Planning covers scope, written authorization, objectives and rules of engagement. Execution is the testing itself: for IaC, that means reviewing templates, modules and pipeline definitions, analyzing change plans and running non-destructive checks in agreed environments. Post-execution covers analysis of results, reporting and mitigation recommendations.
For IaC, planning also includes an inventory: repositories, modules, pipelines, their owners and environments. Without it you can neither choose the depth of testing nor track whether a fix reached the consumers.
A Working Remediation Cycle
On top of the formal phases it helps to keep a practical cycle; this is the author's recommendation, not part of the standard. Each finding becomes a risk statement with a root cause and an owner. Then comes a code-level fix, ideally in the module rather than in one consumer. Then a guardrail that reduces recurrence risk and detects the scenarios it covers: a policy rule, a module test, a mandatory pipeline check. Finally, a retest and documented residual risk, including exceptions with an expiry date.
IaC findings map well onto the control families NIST associates with SP 800-115, such as audit and accountability, system integrity, communications protection, risk assessment, and system and services acquisition: logging, network boundaries, pipeline integrity and module provenance.
Threat Model: Where Infrastructure Code Risk Concentrates
A threat model for IaC does not need to be long. A few areas, each tied to the organization's target outcomes and to root-cause classes, are enough to make prioritization consistent rather than gut-driven.
Identity, Permissions and Network Exposure
Expensive mistakes can look completely harmless in a diff: a role with wildcard permissions "for now," a long-lived service account key instead of federation, an inbound rule with a broad address range, a load balancer that is created public by default. Every such line in a module is multiplied by the number of its consumers. Data protection defaults belong here too: encryption switched off, no log retention, storage access auditing disabled.
Logging deserves particular attention. CISA's Secure by Design guidance treats logging as a capability manufacturers should provide by default and at no extra cost. A shared IaC module is effectively a product for internal teams, and it is reasonable to apply the same principle to it: the module should switch on audit logs, rather than leaving it to each consumer. This is our conclusion by analogy, not a CISA requirement specific to IaC.
Secrets, State Files and Pipeline Logs
Secrets in IaC do not leak only through committed variable files: a value can end up in cleartext in a state file, in a build log through plan output, or in a provider's debug log. So the question "are there secrets in the repository" is not enough. You need to trace the secret's path: where it comes from, where state is stored, who can read the state backend and build artifacts, and how long logs are retained.
The Pipeline as an Attack Surface
A pipeline that applies infrastructure changes holds some of the most powerful credentials in the organization. If untrusted input can reach it, an attacker does not need cloud access; it is enough to make the pipeline do the work.
The classic mechanism is steps that interpolate untrusted data into shell commands: a branch name, a pull request title, a file name. This is the OS command injection class, to which CISA devoted a dedicated Secure by Design Alert. Its alert on directory traversal is relevant wherever a pipeline unpacks or copies artifacts to paths that depend on input. Third-party modules and providers are a separate topic: an unpinned version or a reference to a mutable branch means the module's contents can change without review. NIST SP 800-204D addresses how to integrate software supply chain security into CI/CD pipelines; for more on testing this part, see our piece on software supply chain security assessment.
- External pull requestThe attacker opens a PR from a fork and places a shell command fragment in the branch name or title.
- Interpolation into a CI stepThe planning step inserts that value into a shell command without escaping, and the fragment runs on the runner.
- Runner credentialsThe runner can reach a token that reads state or applies changes, because secrets are not separated from untrusted events.
- Leak or tamperingThe attacker reads a state file containing secrets or alters a module, and the change spreads to every consuming environment.
The assessor examines which events trigger which jobs, which secrets are available to jobs from forks, whether planning and applying are separated, and who gives manual approval for production. These are questions about a technical trust boundary. Process questions, such as who owns the policies and how approvals are recorded, are examined by a process audit, which is different work with different evidence. It is covered in detail in our article on auditing DevSecOps processes.
Drift Between Code and Reality
The last area is the gap between what is described and what is deployed. A manual console edit during an incident, a resource created outside IaC, an organization-level setting that overrides a template: all of these make code an incomplete source of truth. Drift analysis answers not "is the code good" but "can the code be used to judge the environment." In multi-cloud estates, differences between providers' permission models and defaults are added on top, so this part is best examined together with a multi-cloud security assessment.
Assessment Methods and What Each One Cannot See
No single method covers the whole picture, and SP 800-115 explicitly stresses that each technique has its own strengths and limitations. A good assessment combines methods and states honestly which method produced which evidence.
Two practical notes on the table below. Plan analysis requires carefully scoped non-production credentials, and the plan output itself must be stored as confidential data. Test-environment validation also covers the edge: if a template sets security headers on ingress, the test environment is where you confirm they are actually returned.
Scroll sideways to compare
| Method | What it provides | What it cannot see | When it fits |
|---|---|---|---|
| Static analysis of templates and modules | Early detection of common misconfigurations across every consumer of a module | Apply-time values, inherited organization policies, manual changes | Continuously in the pipeline and as the first pass of an audit |
| Change plan analysis | Problems that appear only after a specific environment's variables are substituted | Resources created outside IaC; runtime behavior | Before applying to sensitive environments, using non-production credentials |
| Test environment validation | Confirmation that the described controls are actually deployed and working | Production differences: data, scale, manual edits, inherited settings | For critical identity, network and data storage modules |
| Pipeline and change-permission review | Trust boundaries: events, secrets, approvals, separation of plan and apply | Logic errors inside the templates themselves | Whenever the pipeline has rights to apply changes |
| Manual threat-led review | Dangerous combinations of settings and module design flaws | Completeness across all repositories | For modules with the highest reuse and data sensitivity |
One caveat about test environments: a result obtained in a test environment does not carry over to production unchanged. The test environment confirms that the module is capable of deploying a secure configuration with the given parameters. What is actually deployed in production can only be shown by production's own data: drift analysis, a configuration export, or testing under separate authorization.
Standards as Support, Not Decoration
References to standards are useful exactly to the extent that they help reproduce a conclusion or compare providers; a list of acronyms on the title page proves nothing. For IaC several sources genuinely help, each at its own level.
The NIST Cybersecurity Framework 2.0 is useful at the management level: a target profile describes the outcomes the organization is aiming for, and the code is checked against those outcomes rather than against whatever rule set happens to ship with a scanner. Leadership reporting then speaks about outcomes, not tool output.
The OWASP Application Security Verification Standard provides testable requirements for application technical security controls, including controls in the environment the application relies on. That environment is precisely what IaC describes. For ASVS 5.0.0, the project recommends referring to requirements with versioned identifiers such as v5.0.0-1.2.5, because numbering can change between releases. This lets you link an environment-dependent requirement to the specific module that implements it.
CWE provides a vocabulary of root causes. Grouping findings by class, such as command injection, path traversal, incorrect permission assignment and exposure of sensitive information, shows which mistakes recur across repositories and helps you fix the class rather than the single instance. If a report uses a ranking such as the CWE Top 25, its release year should be stated.
CISA's Secure by Design principles give shared-module owners a reference point: security as a default property, not an option the consumer has to remember to switch on. NIST SP 800-204D provides a framework for integrating supply chain security into CI/CD pipelines, which is useful when an assessment extends beyond templates to how artifacts are built and delivered.
A word on version discipline. SP 800-115 dates from 2008, while ASVS and CWE rankings are updated. Unversioned references make later reproduction and comparison ambiguous, so each reference should be pinned to a version or year, and a methodology document should not be presented as a legal requirement.
Priorities: Root Causes Instead of a Finding Count
The number of findings is a poor measure of an IaC assessment's quality. If one rule fires across a hundred consumers of a shared module, that is one shared root cause with a hundred affected instances: the report should group the root cause while retaining each consumer's exposure and remediation state. Conversely, a single line in a pipeline definition that exposes secrets to fork jobs can outweigh the rest of the report.
Findings should therefore be grouped by root cause, while priority is set by affected scope, exploitability, impact and compensating controls. Shared-module and pipeline defects, external exposure and privilege expansion are separate inputs to that decision. Internal policy deviations that create no exploitable state should form a separate group. SP 800-115 names vulnerability discovery and compliance verification as different purposes, and a report should keep them apart: a mixed list blurs priorities, and teams start fixing what is easier rather than what is more dangerous.
The most valuable outcome of an IaC assessment may turn out to be not a long list but a hardened module or policy rule after which the secure configuration becomes the default. That is the practical reading of Secure by Design: the security decision is made once in the module rather than repeated in every team. If a secure default is impossible, for example because consumers genuinely need different modes, the insecure option should require an explicit, deliberate choice rather than switching on silently, as in the public_read story.
Trade-offs: Block, Warn or Measure
Automated policy checks scale to hundreds of repositories, but they are noisy. Without an exception register in which each exception has an owner, a justification and an expiry, a temporary exception becomes permanent and the rule is effectively disabled. That is exactly how the scanner warning slipped past everyone in the story at the start of this article.
Blocking checks stop risky changes, but they can slow delivery and invite workarounds. Advisory checks preserve speed but depend on someone reading the warning. CISA's Safe Software Deployment guidance emphasizes a deployment process with robust testing and measurement stages. In practice, that argues for a gradual approach: first measure how a rule behaves and how many false positives it produces, then switch high-risk, low-noise rules to blocking mode, and keep the rest advisory under the control of the exception register.
Breadth and depth also compete. A shallow pass across all repositories finds common misconfigurations, while a deep review of a few identity, network and data storage modules finds design flaws that automation misses. A sensible plan directs depth to where reuse and data sensitivity are highest.
Finally, a one-off assessment and continuous guardrails solve different problems. The first provides a well-founded baseline; the second keeps it in place. When procuring a one-off assessment, it is worth asking up front how baseline findings will turn into durable checks rather than remaining a PDF in someone's inbox.
If you are currently deciding what to hand over for external testing, whether a few critical modules, the change-apply pipeline or the entire catalog, it helps to discuss those boundaries in advance. On the pentest scope page you can describe the repositories, environments and constraints, and Pentest.red can help you work out which methods and which evidence make sense to include.
Accepting Fixes and Running the Retest
IaC findings should turn into code changes with a specific owner, not console screenshots. If a fix lands in a shared module, the work does not end at merge: you need to track which consumers are still on the old version, otherwise the fix exists only in the module's repository.
A retest should confirm not only that a specific line changed, but that the guardrail detects the scenarios it covers under the recorded conditions. For more on proving that fixes actually work, see our article on penetration test remediation validation.
Scroll sideways to compare
| Finding class | What to accept as evidence of the fix | How to check during the retest | What to record as residual risk |
|---|---|---|---|
| Insecure default in a shared module | A new module version with a secure default and a test that fails if the old value returns | Change plan analysis for several consumers; a list of consumers still on the old version | Consumers that have not yet updated and their migration date |
| Excessive permissions for a pipeline or service role | A narrowed permission policy and a rule that forbids wildcard permissions | Effective-permission analysis in non-production; attempting a forbidden action in the test environment | Permissions retained under a justified exception, with owner and expiry |
| Command injection in a pipeline step | Passing the value as data through a safe API, or through a correctly quoted environment variable with no eval or shell reinterpretation | Replaying the original scenario with a safe payload on a test pipeline; checking neighboring steps of the same template | Pipeline templates that could not be migrated and compensating controls |
| Secret in a state file or log | Secret rotation, move to a secrets manager, restricted access to the state backend | Searching for the value in new state and logs; checking who can read the backend | The period during which the old secret was exposed and the decision on further action |
| Unpinned third-party module or provider | Version or hash pinning, a lock file in the repository, a policy rule | Confirming the build fails when an unpinned source is used | Modules without a supported version and a plan to replace them |
Operational metrics are worth choosing in advance: the share of modules passing the agreed policies, time to remediate by severity, and the number of expired exceptions. These are recommended practices, not reference values; define them against your own target profile. Machine-readable ASVS requirement lists let you tag policy rules with requirement identifiers, and CWE identifiers let you group findings by root cause.
If AI-based tools are involved in the analysis, a qualified specialist must review their output before it influences remediation or reporting: a plausible but wrong explanation of why a rule is "safe" replicates just as easily as a mistake in a module.
Choosing a Provider for an IaC Security Assessment
Comparing proposals is pointless if it is unclear what exactly will be tested. Two documents written for buyers help here: CISA's Secure by Demand guide, designed for negotiations with software and service providers, and the joint guidance from Australia's ASD's ACSC and CISA on choosing secure and verifiable technologies. Both work well as the skeleton of a request for proposals.
ASVS helps here too: the project states that its aim is to normalize the coverage and rigor of security verification services. If you ask each provider to state which requirements and levels it covers, with identifiers and version, proposals become comparable.
Practical questions are worth asking before signing. Which versions of which standards does the provider rely on, and what is each used for in the method? How are authorization, rules of engagement and the list of permitted environments documented? How are state files, secrets and plan output protected and deleted after the project? Will results be delivered in machine-readable form, mapped to CWE and to versioned requirement identifiers? Are findings grouped by root cause, with module-level fixes? Do the retest and help with pipeline guardrails form part of the work?
The warning signs are recognizable too: a report consisting only of scanner output; unversioned references to standards; claims about certifications, clients or results that cannot be verified; willingness to test production without written consent. The last is sufficient grounds to walk away. And remember that a pentest or security assessment is not certification or attestation: it is a technical opinion on the risks found at the time of testing within the agreed scope, not a guarantee that none exist.
The choice between in-house effort and an external provider depends on maturity and independence requirements. A team with well-established policy-as-code can run continuous checks itself, while an external assessment is useful for the first baseline, before risky architecture changes, and where policy or contract requires independence. One available option is a hybrid model: an external baseline and a deep review of critical modules, followed by internal guardrails and periodic external retests. If that format suits you, it can be discussed with Pentest.red as part of penetration testing services: which modules and pipelines go into the baseline, what evidence you will receive, and how the retest will work.
Conclusion: Keeping an IaC Assessment Useful After the Report
An infrastructure as code security assessment is not a scanner run against a repository but a technical assessment following the logic of NIST SP 800-115, applied to templates, modules and pipelines. It measures code against the organization's target outcomes, groups findings by root cause and sets priorities by risk and pursues secure defaults instead of endlessly finding the same mistakes.
Whether you run the audit yourself or commission it, insist on four things: written authorization with clear boundaries, versioned references to standards, findings grouped by root cause, and module-level fixes together with guardrails that reduce recurrence risk and detect the scenarios they cover. And keep the evidence boundary in mind: code, plan, test environment and production are four different levels, and an honest report always says at which of them each conclusion was reached.
PENTEST.RED
Discuss pentest scope and pricing
We start by agreeing on systems, access, constraints and the expected evidence.
Based on in-depth research, edited for clarity and practical use.