Teams that rely only on infrastructure as code scanning miss everything changed in the console, and teams that rely only on runtime cloud scanning keep finding the same misconfiguration every time the pipeline redeploys it. IaC scanning checks Terraform, CloudFormation, Bicep, Kubernetes manifests and similar files before deployment; runtime cloud scanning (the posture checks inside CSPM and CNAPP tools and the providers' native services) reads the live control plane after deployment. Each sees a different version of the truth, and the gap between them, drift and click-ops, is where many cloud incidents start. This guide compares the two by threat model, architecture, what each misses and failure modes, then shows how to reconcile findings across both, how to close the feedback loop, and how secrets in IaC change the picture. It is for cloud security engineers, platform teams and DevSecOps leads. For tool mechanics, see the Checkov and IaC Scanners Guide and the Cloud Security Posture Management (CSPM) Guide. Where provider features are named, platforms are listed alphabetically; order is not a ranking.
Key takeaways
- The short answer is intent versus reality. IaC scanning checks what you meant to deploy; runtime scanning checks what is actually deployed. You need both because they disagree whenever anything changes outside the pipeline.
- IaC scanning is cheapest per fix but blind to anything not in code. Console edits, emergency changes, resources created by other tools and service defaults never appear in the files it reads.
- Runtime scanning sees everything but fixes nothing durably on its own. If the source of a finding is a Terraform module, fixing it in the console creates drift that the next apply silently reverts.
- Drift detection is the bridge. Comparing live state with declared configuration tells you whether a runtime finding belongs to code, to click-ops or to an unmanaged resource, and therefore who fixes it and where.
- Secrets in IaC are a third problem. Credentials in variables, state and plan files are non-human identity exposures that neither misconfiguration rule set fully covers; they need secret scanning and state protection.
What each approach is
- IaC scanning: static analysis of infrastructure definitions in the developer's editor, at pull request and in the pipeline, using policy rules for settings such as public storage, open security groups, unencrypted volumes and wildcard IAM policies. Mature setups scan the rendered plan as well as source, because variables, modules and conditionals only resolve at plan time. Admission controllers and provider guardrails that evaluate requests at deploy time extend the same idea to the moment of creation.
- Runtime cloud scanning: continuous reading of provider APIs and configuration records to evaluate deployed resources against policies, raising findings whether the resource came from code, a console, a script or another team's pipeline. This is the core of CSPM and the posture half of CNAPP.
- Drift detection: comparing deployed resources with their IaC definition. It is related to both but is neither: it does not judge whether a setting is safe, only whether reality still matches intent. See the Configuration Drift and Baseline Enforcement Guide.
Side-by-side comparison
| Dimension | IaC scanning | Runtime cloud scanning |
|---|---|---|
| Object inspected | Templates, modules, manifests, plan files | Live resources in accounts, subscriptions and projects |
| When it runs | Editor, pull request, pipeline, pre-apply | On configuration change events and on schedules |
| Question answered | Will this definition create something unsafe? | Is anything running now configured unsafely? |
| Coverage | Only resources defined in scanned repositories | Every resource the scanner role can read |
| Context available | Author, commit, module, intent; little runtime context | Actual exposure, attached identities, data, network paths |
| Fix location | The code, by the author, before deployment | Often the console or a ticket, unless traced back to code |
| Cost of a fix | Low: change a line before anything exists | Higher: change a live system, often under change control |
| False positive source | Unresolved variables, compensating controls elsewhere | Intentional exceptions, missing business context |
| Typical owner | Developers and platform engineering | Cloud security and governance |
Threat model: what each defends against
IaC scanning defends against engineered-in risk: a module that creates public buckets by default, a pipeline that opens port 22 to 0.0.0.0/0, an IAM policy with "Action": "*". It prevents the misconfiguration from ever reaching an account, and it scales because one fixed module fixes every future deployment. The Top 10 Infrastructure as Code Misconfigurations lists the patterns it catches best.
Runtime scanning defends against everything else: an engineer opening a security group during an outage and forgetting to close it, a storage bucket created by hand for a demo, a resource deployed by a vendor's integration, or an attacker changing configuration. ATT&CK v20 covers several of these actions: T1578 Modify Cloud Compute Infrastructure, T1098 Account Manipulation for cloud identities, and T1530 Data from Cloud Storage once exposure exists. None of those changes passes through your pipeline, so only runtime visibility sees them.
Neither sees the other's blind spot. A clean IaC scan proves only that the code was clean at merge time; a clean runtime scan proves only that today's live configuration passes the rules the scanner has.
Architecture in words
IaC scanning path: developer writes HCL or YAML; a local hook or IDE plugin runs rules; the pull request runs the scanner against source; the pipeline runs terraform plan, exports the plan as JSON and scans it so module outputs and variables are resolved; a policy gate blocks or warns; the apply runs with a deployment identity. Results go back to the pull request where the author can act.
Runtime scanning path: a read-only role in each account, subscription or project lets the scanner list resources and read configuration; change events trigger near real-time checks for supported resources and full scans run on a schedule. Google Cloud's Security Health Analytics, for example, runs real-time detectors on configuration change for supported resources and batch scans on a daily or 48-hour cycle depending on tier. Findings are routed to tickets, a security hub or chat channels.
Drift path: the IaC platform periodically runs a speculative plan against live infrastructure. HashiCorp's HCP Terraform health assessments do this on a schedule, report configuration drift with proposed changes, and let you either overwrite the drift by applying code or update code to accept the change; they are available in its Standard and Premium editions and do not modify infrastructure. AWS CloudFormation drift detection compares stack resources with the template on request and reports MODIFIED, DELETED or IN_SYNC per resource.
What each misses
| Blind spot | IaC scanning | Runtime scanning |
|---|---|---|
| Console and CLI changes (click-ops) | Misses entirely | Sees the result, not who or why without audit logs |
| Resources never defined in code | Misses entirely | Sees them, cannot say they are unmanaged without IaC inventory |
| Service defaults not written in code | Often misses; rules may flag only explicit settings | Sees the effective setting |
| Values resolved only at plan or apply | Misses in source-only scans; plan scanning closes most | Sees the final value |
| Ownership and intent | Knows the author and commit | Usually lacks it unless tagged |
| Future deployments of the same pattern | Prevents them | Finds each one after it exists |
| Secrets committed in variables or state | Partial: needs a secret scanner | Rarely reads repositories or state |
| Combined exposure (public, plus privileged identity, plus sensitive data) | Limited to the files in one repo | Strong when the tool builds a resource graph |
Drift tools have their own limits. CloudFormation compares only properties set explicitly in the template, not service defaults, and does not cover nested stacks from the parent; HCP Terraform detects configuration drift, not state-only drift. Write security-relevant settings explicitly in code, even when they match the default, so drift on them is visible.
Drift and click-ops: reconciling the two finding streams
The practical problem is that the same misconfiguration appears twice, or appears in one stream with no match in the other. Classify every runtime finding into one of four cases before anyone fixes it.
| Case | How to recognise it | Where to fix |
|---|---|---|
| Code-origin | Resource is IaC-managed, no drift, and the IaC scanner also flags it (or should) | The module or template; add or tune the IaC rule |
| Drift | Resource is IaC-managed but live setting differs from code | Decide: revert by re-applying code, or accept by changing code; never only in the console |
| Unmanaged | Resource has no IaC owner tag or state entry | Import into code or delete; fix interim exposure in place |
| Rule gap | IaC scan passed and no drift, but runtime flags it | Add the equivalent rule to the IaC policy set |
- Tag everything at creation: enforce tags such as repository, workspace or stack and owner on IaC-created resources so the runtime scanner can link a finding to code.
- Join the inventories: export IaC state resource IDs and match them to the runtime inventory; the remainder is your unmanaged list.
- Align rule sets: map each runtime policy to an IaC rule, or record why it cannot exist pre-deploy (for example a rule that depends on observed traffic).
- Route by case: code-origin and rule gaps go to the repository as a pull request or issue; drift goes to the workspace owner; unmanaged goes to the cloud platform team.
- Fix the console edit forward: when an emergency change is legitimate, raise the matching code change within an agreed window so the next apply does not undo it.
Feedback loops and guardrails
A finding fixed only in runtime will return. The loop that works: runtime finding, classify, if code-origin or rule gap then update the module and the IaC rule, then confirm the runtime finding closes after the next deployment. Track how many runtime findings each month produce a new or tightened IaC rule; that number shows the left side is learning.
Deploy-time guardrails sit between the two. Microsoft Azure Policy's deny effect blocks a matching create or update request with a 403 before it reaches the resource provider, whichever tool sent it, and marks existing matching resources non-compliant without changing them. AWS Config proactive evaluation checks a set of resource properties before deployment for supported resource types, while detective evaluation covers resources already deployed on change or on a schedule. Guardrails stop click-ops as well as pipeline mistakes, so they are the preferred home for the highest-impact rules. Roll them out in audit or report mode first, review the impact on running deployments, then enforce by scope; roll back by moving a scope back to audit rather than deleting the assignment.
Secrets in IaC
Infrastructure code is a common place for non-human identity credentials to leak: provider keys in variables files, database passwords in modules, tokens in pipeline configuration. HashiCorp documents that Terraform stores resource attributes, including secrets placed in configuration, in state and plan files; marking a value sensitive hides it from output but does not keep it out of state. Ephemeral values (Terraform 1.10 and later) and provider-defined write-only arguments (Terraform 1.11 and later) are omitted from state and plans. HashiCorp recommends remote state, encryption at rest, access control and audit logging when state holds sensitive values.
- Pre-commit and push: run secret scanning on IaC repositories; GitHub push protection, for example, blocks pushes containing supported secret patterns, and bypasses are logged and alerted.
- State and plans: treat state files and saved plan artefacts as secrets stores, and scan them. See How to find secrets in Terraform state.
- Pipeline identity: replace static cloud keys in the deployment pipeline with workload identity federation. See the Terraform and IaC Pipeline Credential Security Guide.
- Runtime side: runtime scanners can flag long-lived keys and over-privileged deployment roles in the account, which is where the IaC pipeline identity shows up. See IaC identity policy checks.
Decision criteria and implementation order
Mostly IaC-managed estate: start with IaC scanning on pull requests and plan files, add drift detection, then runtime scanning to catch the residue. Mostly click-ops or inherited estate: start with runtime scanning for immediate visibility, then use the unmanaged list to drive import into code. Small team: the providers' native posture services plus one open source IaC scanner in the pipeline cover most of the value; spend effort on tagging and the reconciliation table above. Large or regulated estate: add a resource graph that joins findings to identities and data, organisation-wide guardrails, and drift reporting as an audit artefact.
- Weeks 0 to 4: turn on runtime scanning in every account in read-only mode; run IaC scanning in warn mode on main repositories; enforce ownership tags.
- Weeks 4 to 8: join inventories, produce the unmanaged list, scan plan files as well as source, add secret scanning to IaC repositories.
- Months 2 to 4: switch IaC gates to block for high-severity rules with a documented exception path; enable drift detection on production workspaces and stacks.
- Months 4 to 9: move the top runtime rules into deploy-time guardrails in audit mode, then enforce; start the monthly rule-gap review.
Worked scenario
A runtime scanner flags a storage bucket allowing public read in production. The IaC scan for the same repository passed last week. The engineer checks tags: the bucket belongs to a Terraform workspace. The drift report shows the bucket policy was changed outside Terraform two days ago; the cloud audit log shows a support engineer added public read to share a file with a customer. Classification: drift. The owner removes public access by re-applying code, adds the "no public bucket policy" rule to the organisation guardrail in audit mode, and the support team gets an approved file sharing route. A second finding the same day, a database with deletion protection off, is IaC-managed with no drift and no IaC rule: a rule gap. The team adds the rule, and the scanner flags the same setting in four other modules before they reach production.
Detection and measurement
| Metric | What it shows |
|---|---|
| Share of runtime findings classified as drift or unmanaged | Size of the click-ops gap |
| Share of cloud resources with an IaC owner | How much the left side can actually prevent |
| Recurring findings (same rule, same resource, reopened after fix) | Fixes applied in the console instead of code |
| Runtime findings per month that produce a new IaC rule | Health of the feedback loop |
| Median time from drift detected to reconciled | Discipline of the drift process |
| Secrets found in IaC repositories, state or plans per month | NHI exposure in the delivery path |
Log and alert on: console changes to IaC-tagged resources (from the provider audit log), IaC gate bypasses and exceptions, guardrail denials by principal, and read access to state storage.
Common failure patterns
- Fixing in the console: the next apply reverts the fix, or the fix becomes undocumented drift that breaks the next deployment.
- Two rule sets that disagree: IaC and runtime policies written by different teams flag different things, so developers lose trust in both.
- Source-only scanning: modules and variables resolve at plan time, so real values are never checked.
- No unmanaged inventory: resources outside code are never brought under control and runtime findings on them recur.
- Treating
sensitiveas secret protection: values still sit in state and plans readable by anyone with access to them. - Over-privileged scanners and pipelines: the deployment identity and the scanner role are themselves high-value NHIs; see Securing the CNAPP and CSPM platform itself. The Capital One breach shows how an over-permissioned cloud role turns one misconfiguration into a large data loss.
Framework mapping
| Framework | IaC scanning | Runtime cloud scanning and drift |
|---|---|---|
| NIST SP 800-53 Rev. 5 | CM-3 Configuration Change Control; SA-11 Developer Testing and Evaluation; CM-2 Baseline Configuration | CM-6 Configuration Settings; CA-7 Continuous Monitoring; CM-2 Baseline Configuration |
| NIST CSF 2.0 | PR.PS-06 secure software development practices; PR.PS-01 configuration management | DE.CM-09 monitoring of computing and runtime environments; ID.RA-01 vulnerabilities identified |
| CIS Controls v8.1 | Control 16 Application Software Security; Control 4 Secure Configuration | Control 4 Secure Configuration; Control 1 Inventory and Control of Enterprise Assets |
| ISO/IEC 27001:2022 Annex A | 8.25 Secure development life cycle; 8.32 Change management | 8.9 Configuration management; 8.16 Monitoring activities |
Practitioner checklist
- Run IaC scanning on pull requests and on rendered plan files, not source alone.
- Enable runtime scanning in every account, subscription and project with a read-only role.
- Enforce owner and repository tags so runtime findings link back to code.
- Classify each runtime finding as code-origin, drift, unmanaged or rule gap before fixing.
- Turn on drift detection for production workspaces and stacks; write security settings explicitly in code.
- Promote the highest-impact rules to deploy-time guardrails, audit mode first.
- Scan IaC repositories, state and plan files for secrets, and use ephemeral or write-only values where supported.
- Review each month which runtime findings produced new IaC rules.
Standards and references
- HashiCorp: HCP Terraform health assessments
- HashiCorp: Manage sensitive data in Terraform
- AWS: Detect unmanaged configuration changes with CloudFormation drift detection
- AWS: AWS Config rule evaluation modes and trigger types
- Google Cloud: Security Health Analytics overview
- Microsoft Learn: Azure Policy deny effect
- GitHub Docs: About push protection
Related NHI Mgmt Group resources: Infrastructure-as-Code Scanning Buyer's Guide, CWPP vs CSPM, CNAPP Guide, AWS Config Guide, Cloud Security Guide.