IaCFeb 18, 2026 · 11 min read

Infrastructure as Code Security: Catching Misconfigurations Before They Reach Production

Terraform, CloudFormation, and Kubernetes manifests can contain critical misconfigurations. Learn how to scan and fix them before deployment.

SW

SafeWeave Team

Every organization that has adopted cloud infrastructure at scale eventually arrives at the same realization: manual configuration does not scale, and the mistakes born from manual processes are both inevitable and expensive. Infrastructure as Code (IaC) emerged as the answer, allowing teams to define servers, networks, databases, and policies in declarative configuration files that can be versioned, reviewed, and deployed reproducibly. Terraform, AWS CloudFormation, Kubernetes manifests, Pulumi, and Ansible playbooks have become the lingua franca of modern cloud engineering.

But here is the uncomfortable truth that the industry is still coming to terms with: IaC does not eliminate misconfigurations. It codifies them. A security group rule that opens port 22 to the entire internet is just as dangerous when it lives in a .tf file as when someone clicks through the AWS console. The difference is that in an IaC world, that misconfiguration can be replicated across every environment, committed to version control, and deployed automatically by a CI/CD pipeline, all before a human being ever thinks to question it.

This article examines the most critical security risks in Infrastructure as Code, walks through the real-world misconfigurations that lead to breaches, and outlines practical approaches to catching those misconfigurations before they reach production. Whether you are writing Terraform modules, authoring CloudFormation templates, or defining Kubernetes manifests, the principles and patterns here will help you build a more secure infrastructure pipeline.

Why Infrastructure as Code Security Matters Now More Than Ever

The 2024 Cloud Security Alliance report found that misconfigured cloud infrastructure accounted for 45% of all cloud data breaches. That number should be alarming, but it becomes even more concerning when you consider the accelerating trend of AI-assisted infrastructure development. Engineers are increasingly using AI coding assistants to generate Terraform modules, Kubernetes deployments, and CloudFormation stacks. These tools are remarkably productive, but they also introduce a particular kind of risk: the generated code often works correctly in a functional sense while being deeply insecure.

An AI assistant asked to "create an S3 bucket for storing user uploads" will happily produce a bucket configuration that works. It will probably not add server-side encryption, enable versioning, configure access logging, or restrict public access, unless you explicitly ask for those things. The gap between "functional" and "secure" is precisely where IaC vulnerabilities thrive.

OWASP identifies security misconfiguration as one of its Top 10 risks (A05:2021), and the CWE database catalogs dozens of weakness types related to infrastructure misconfiguration, including CWE-16 (Configuration) and CWE-1188 (Insecure Default Initialization of Resource). These are not theoretical concerns. They are the root causes behind some of the most significant breaches of the past decade.

The Anatomy of IaC Misconfigurations

Understanding IaC security requires understanding the categories of misconfigurations that occur most frequently. These fall into several broad patterns that recur across every IaC tool and cloud provider.

Overly Permissive Access Controls

The single most common IaC misconfiguration is granting more access than necessary. This manifests differently across tools, but the pattern is universal.

In Terraform, this often looks like an IAM policy with wildcard permissions:

resource "aws_iam_policy" "app_policy" {
  name = "app-policy"
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect   = "Allow"
        Action   = "*"
        Resource = "*"
      }
    ]
  })
}

This policy grants full administrative access to every AWS service and every resource in the account. It is the IAM equivalent of leaving every door in a building unlocked and propped open. Yet variations of this pattern appear in production codebases with alarming regularity, particularly when developers are iterating quickly and plan to "tighten permissions later."

The secure version scopes permissions to exactly what the application requires:

resource "aws_iam_policy" "app_policy" {
  name = "app-policy"
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "s3:GetObject",
          "s3:PutObject"
        ]
        Resource = "arn:aws:s3:::my-app-bucket/*"
      },
      {
        Effect = "Allow"
        Action = [
          "dynamodb:GetItem",
          "dynamodb:PutItem",
          "dynamodb:Query"
        ]
        Resource = "arn:aws:dynamodb:us-east-1:123456789012:table/my-app-table"
      }
    ]
  })
}

Unencrypted Data Storage and Transit

Cloud storage services default to different encryption states depending on the provider and service. Relying on defaults without explicitly configuring encryption is a misconfiguration that can have compliance implications under frameworks like HIPAA, PCI DSS, and GDPR.

A common Terraform pattern that creates an unencrypted RDS instance:

resource "aws_db_instance" "database" {
  allocated_storage    = 20
  engine              = "postgres"
  engine_version      = "15.4"
  instance_class      = "db.t3.micro"
  db_name             = "appdb"
  username            = "admin"
  password            = "SuperSecret123!"  # Hardcoded credential!
  skip_final_snapshot = true
}

This snippet contains multiple issues: the password is hardcoded in plain text (CWE-798: Use of Hard-coded Credentials), encryption at rest is not enabled, the instance is potentially publicly accessible by default, automated backups are not explicitly configured, and skip_final_snapshot means data will be lost on destruction. Each of these issues is independently significant. Together, they represent a database that is one misconfigured security group away from a breach.

Publicly Exposed Resources

Another pervasive pattern is the inadvertent public exposure of resources that should be private. In Kubernetes, this manifests as services exposed with LoadBalancer type without proper network policies:

apiVersion: v1
kind: Service
metadata:
  name: internal-api
spec:
  type: LoadBalancer    # Exposes to public internet
  selector:
    app: internal-api
  ports:
    - port: 80
      targetPort: 8080

An internal API service should almost never be of type LoadBalancer. This creates a publicly accessible endpoint for what the name itself tells us should be internal. The corrected version uses ClusterIP (the default) or an internal load balancer annotation:

apiVersion: v1
kind: Service
metadata:
  name: internal-api
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-internal: "true"
spec:
  type: ClusterIP
  selector:
    app: internal-api
  ports:
    - port: 80
      targetPort: 8080

Missing Security Boundaries in Kubernetes

Kubernetes misconfigurations deserve special attention because of the blast radius they create. A pod running as root with a host path mount can compromise an entire node, and from there, potentially the entire cluster.

Consider this deployment manifest:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: app
          image: myapp:latest
          securityContext:
            privileged: true        # Full host access
            runAsUser: 0            # Running as root
          volumeMounts:
            - name: host-fs
              mountPath: /host
      volumes:
        - name: host-fs
          hostPath:
            path: /                 # Mounts entire host filesystem

This deployment runs a privileged container as root with the entire host filesystem mounted. An attacker who gains code execution inside this container effectively has root access to the underlying node. This maps directly to CWE-250 (Execution with Unnecessary Privileges) and violates multiple CIS Kubernetes Benchmark recommendations.

A secure version applies the principle of least privilege:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        fsGroup: 1000
        seccompProfile:
          type: RuntimeDefault
      containers:
        - name: app
          image: myapp:v1.2.3       # Pinned version, not :latest
          securityContext:
            privileged: false
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop:
                - ALL
          resources:
            limits:
              memory: "256Mi"
              cpu: "500m"
            requests:
              memory: "128Mi"
              cpu: "250m"

CloudFormation-Specific Risks

AWS CloudFormation introduces its own set of security considerations. One common pattern is creating security groups with overly permissive ingress rules:

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  WebServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Web server security group
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 0
          ToPort: 65535
          CidrIp: 0.0.0.0/0     # All ports open to the world

This opens every TCP port to the entire internet. A corrected version restricts ingress to only the necessary ports and, ideally, to known IP ranges or other security groups:

AWSTemplateFormatVersion: '2010-09-09'
Resources:
  WebServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Web server security group
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0     # HTTPS only
      SecurityGroupEgress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0

Catch these vulnerabilities automatically with SafeWeave

SafeWeave runs 8 security scanners in parallel — SAST, secrets, dependencies, IaC, containers, DAST, license, and posture — right inside your AI editor. One command, zero config.

Start Scanning Free

Scanning Approaches for IaC Security

Catching these misconfigurations requires a multi-layered scanning approach. No single tool or technique catches everything, and the most effective security programs combine several approaches at different stages of the development lifecycle.

Static Analysis of IaC Templates

The most foundational approach is static analysis of IaC files before they are applied. Tools in this category parse Terraform, CloudFormation, Kubernetes YAML, and other formats, then evaluate them against a library of security rules.

The key capability here is catching misconfigurations at the earliest possible point, before infrastructure is provisioned. This is analogous to SAST (Static Application Security Testing) for application code, but applied to infrastructure definitions. The rules typically map to compliance frameworks like CIS Benchmarks, SOC 2 requirements, and HIPAA controls.

Static IaC scanning should be integrated at two points: in the developer's local environment (IDE or pre-commit hooks) and in the CI/CD pipeline as a gate. The local integration provides immediate feedback; the pipeline integration provides enforcement.

Plan-Time Analysis

For Terraform specifically, analyzing the plan output provides security insights that static analysis alone cannot. The Terraform plan shows what will actually change, including computed values that are not visible in the .tf files themselves. A plan-time scanner can detect that a planned change would remove encryption from a database or add a public IP to a previously private instance.

This approach requires running terraform plan and parsing the output, which means it is typically implemented in CI/CD rather than in the IDE. But it catches a class of issues that static analysis misses, particularly around module composition and variable interpolation.

Runtime Posture Assessment

Even with thorough pre-deployment scanning, runtime assessment remains essential. Configuration drift, manual changes made through the console, and changes from other IaC pipelines can introduce misconfigurations that were not present in the scanned code.

Cloud Security Posture Management (CSPM) tools continuously assess the actual state of cloud infrastructure against security policies. The most effective programs compare the intended state (from IaC) with the actual state (from cloud APIs) and alert on discrepancies.

Policy as Code

Policy as Code frameworks like Open Policy Agent (OPA) and HashiCorp Sentinel allow organizations to codify their security requirements as executable policies. This is particularly valuable for organizations with complex compliance requirements because it allows security teams to express requirements once and enforce them automatically.

An OPA policy that prevents public S3 buckets looks like this:

package terraform.s3

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_s3_bucket"
  resource.change.after.acl == "public-read"
  msg := sprintf("S3 bucket '%s' must not be publicly readable", [resource.name])
}

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_s3_bucket_public_access_block"
  resource.change.after.block_public_acls != true
  msg := sprintf("S3 bucket '%s' must block public ACLs", [resource.name])
}

This policy can be evaluated against a Terraform plan to prevent any deployment that would create a publicly accessible S3 bucket.

Integrating IaC Security into the Development Workflow

The effectiveness of IaC security scanning is directly proportional to how well it integrates into the developer's existing workflow. Scanning that requires developers to switch tools, run separate commands, or interpret unfamiliar output is scanning that will be ignored or circumvented.

Shift-Left: IDE and Pre-Commit Integration

The earliest and most effective intervention point is the developer's IDE. When a misconfiguration is flagged as the developer writes the code, the cost of fixing it is measured in seconds. When the same misconfiguration is caught in a production security audit, the cost is measured in days or weeks.

Pre-commit hooks provide a lightweight enforcement mechanism that catches misconfigurations before code even enters version control:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/antonbabenko/pre-commit-terraform
    rev: v1.88.0
    hooks:
      - id: terraform_fmt
      - id: terraform_validate
      - id: terraform_tflint
      - id: terraform_checkov
        args: ['--args=--framework terraform --compact']

This configuration runs formatting checks, validation, linting, and security scanning automatically on every commit. Developers get immediate feedback without needing to run separate tools.

For teams using AI coding assistants to generate infrastructure code, this integration point is particularly critical. The AI may generate a functional but insecure Terraform module, and the pre-commit hook catches the security issues before the developer even commits the code. Tools like SafeWeave extend this concept further by integrating security scanning directly into the AI-assisted workflow, catching misconfigurations at the moment of generation rather than at commit time.

CI/CD Pipeline Gates

The CI/CD pipeline serves as the enforcement layer for IaC security. While IDE and pre-commit integrations are advisory (developers can skip them), pipeline gates are mandatory. A well-designed pipeline prevents insecure infrastructure from being deployed, regardless of how it was authored.

A typical pipeline stage for IaC security looks like this:

# .github/workflows/terraform.yml
name: Terraform Security
on:
  pull_request:
    paths:
      - 'infrastructure/**'

jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run IaC security scan
        uses: bridgecrewio/checkov-action@master
        with:
          directory: infrastructure/
          framework: terraform
          output_format: sarif
          soft_fail: false

      - name: Terraform Plan
        run: |
          cd infrastructure
          terraform init
          terraform plan -out=plan.tfplan
          terraform show -json plan.tfplan > plan.json

      - name: Analyze Plan
        run: |
          checkov -f plan.json --framework terraform_plan

This pipeline runs static analysis on the Terraform files and then analyzes the plan output, catching both categories of issues before any infrastructure change is applied.

Pull Request Annotations

Modern IaC security tools can annotate pull requests directly, showing exactly which lines of infrastructure code introduce security issues. This transforms the code review process by giving reviewers security context alongside the functional changes.

When a pull request modifies a Terraform file to add a new security group rule, the annotation can show that the rule opens a port to the internet, which CIS Benchmark control it violates, and what the recommended fix is, all inline with the code diff.

Compliance Mapping for IaC Security

IaC misconfigurations do not exist in a vacuum. They map to specific compliance requirements that organizations must meet. Understanding these mappings helps security teams prioritize their scanning rules and helps developers understand why certain configurations are required.

CIS Benchmarks

The Center for Internet Security publishes benchmarks for AWS, Azure, GCP, and Kubernetes that map directly to IaC configurations. For example, CIS AWS Benchmark 2.1.1 requires that S3 bucket server-side encryption is enabled. This translates to a scanning rule that checks for the aws_s3_bucket_server_side_encryption_configuration resource in Terraform.

SOC 2

SOC 2 Trust Services Criteria require that systems are protected against unauthorized access (CC6.1) and that changes are controlled and authorized (CC8.1). IaC security scanning provides evidence for both: it demonstrates that infrastructure configurations are reviewed for security (CC6.1) and that changes go through an automated review process (CC8.1).

HIPAA

For organizations handling protected health information, HIPAA requires encryption of data at rest and in transit (45 CFR 164.312(a)(2)(iv) and 164.312(e)(1)). IaC scanning rules that enforce encryption configurations on databases, storage buckets, and load balancers directly support HIPAA compliance.

PCI DSS

PCI DSS v4.0 requirement 2.2 mandates that system components are configured securely. IaC security scanning that enforces configuration standards provides continuous evidence of compliance with this requirement.

Common Pitfalls and How to Avoid Them

The "Fix It Later" Pattern

Perhaps the most dangerous pattern in IaC security is the practice of marking security findings as "to be addressed later." In fast-moving teams, this often means never. A misconfiguration that is known but not fixed is arguably worse than one that is unknown, because it represents a conscious decision to accept risk.

The solution is to make security scanning a hard gate in the pipeline. If the scan fails, the deployment does not proceed. This requires calibrating the scan rules carefully, too many false positives lead to developers disabling the gate entirely, but the principle of not deploying known-insecure infrastructure should be non-negotiable.

Module Trust Issues

Terraform modules from the public registry and Helm charts from public repositories present a particular trust challenge. These modules were written by third parties with potentially different security requirements. Using them without reviewing their security posture is equivalent to running untrusted code in production.

The mitigation is to scan modules after composition. Rather than trusting that a public module is secure, scan the final composed configuration that includes all module outputs and variable interpolations. This catches insecure defaults in modules that are not overridden by the consuming configuration.

Secrets in IaC Files

Despite being one of the most well-understood security anti-patterns, hardcoded secrets in IaC files remain common. Database passwords, API keys, and TLS certificates embedded in Terraform variables, CloudFormation parameters, and Kubernetes secrets (which are base64-encoded, not encrypted) continue to be committed to version control.

IaC security scanning must include secret detection as a first-class capability. This means scanning not just for known secret patterns (AWS access keys, private keys, etc.) but also for variable names that suggest secrets (password, secret, api_key) that are assigned literal values rather than references to secret management systems.

SafeWeave includes secret detection as one of its eight integrated scanners, catching hardcoded credentials in IaC files alongside the misconfiguration scanning that identifies insecure resource definitions. This dual approach is important because a Terraform file might be free of misconfigurations but still contain a hardcoded database password, and both issues need to be caught.

State File Security

Terraform state files contain the actual values of every attribute of every managed resource, including secrets. The state file for the insecure database example above would contain the plaintext password. State files must be stored in encrypted backends (S3 with SSE, Azure Blob with encryption, GCS with CMEK) and access must be restricted through IAM policies.

This is a meta-concern that IaC scanning tools should check for: is the backend configuration itself secure?

terraform {
  backend "s3" {
    bucket         = "my-terraform-state"
    key            = "prod/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true                    # State encryption enabled
    dynamodb_table = "terraform-locks"       # State locking enabled
    kms_key_id     = "alias/terraform-state" # Customer-managed key
  }
}

The Role of AI in IaC Security

The rise of AI-assisted development has introduced a new dimension to IaC security. AI coding assistants generate infrastructure code with increasing frequency, and the code they generate reflects the patterns in their training data, which includes both secure and insecure examples.

This creates an interesting dynamic: AI tools can accelerate both the creation of infrastructure code and the introduction of security issues. A developer who asks an AI to "create a Kubernetes deployment for a web application" may receive a deployment that lacks resource limits, runs as root, and uses the latest tag, all common patterns in tutorial code that the AI has learned from.

The answer is not to stop using AI for infrastructure code. The productivity gains are too significant to ignore. The answer is to pair AI-assisted infrastructure development with automated security scanning that catches the issues the AI introduces. This is the approach that SafeWeave takes: rather than trying to prevent insecure code from being generated, it scans the generated code in real time and provides actionable remediation guidance.

This pairing of AI generation with automated security scanning represents a new paradigm in infrastructure security. The developer gets the speed benefit of AI-assisted development, the security team gets confidence that infrastructure changes are reviewed for security, and the organization gets the compliance evidence it needs, all without adding manual review bottlenecks.

Try SafeWeave in 30 seconds

npx safeweave-mcp

Works with Cursor, Claude Code, Windsurf, and VS Code. No signup required for the free tier — 3 scanners, unlimited scans.

Building a Mature IaC Security Program

Implementing IaC security is not a binary state. Organizations typically progress through several levels of maturity.

Level 1: Ad-Hoc Scanning

Teams run IaC security tools manually, typically in response to a security incident or audit finding. Scanning is not consistent, and results are not tracked over time.

Level 2: Automated Scanning

IaC security scanning is integrated into the CI/CD pipeline and runs automatically on every change. Results are reported but may not block deployments.

Level 3: Enforced Policies

Security scanning is a hard gate in the pipeline. Failed scans prevent deployment. Policies are codified and version-controlled alongside the infrastructure code.

Level 4: Continuous Compliance

IaC security scanning is combined with runtime posture assessment. The organization can demonstrate continuous compliance with relevant frameworks. Drift detection identifies manual changes that deviate from the codified state. Findings are tracked, prioritized, and resolved within defined SLAs.

Level 5: Proactive Security

Security requirements are embedded in the infrastructure development process from the start. Module libraries are pre-approved and pre-hardened. AI-assisted development is paired with automated scanning. Security is a shared responsibility, not a gate.

Most organizations are somewhere between Level 1 and Level 2. The goal should be to progress to at least Level 3, where insecure infrastructure is prevented from being deployed, and to aspire to Level 4 and 5, where security is continuous and proactive.

Conclusion

Infrastructure as Code has fundamentally changed how organizations manage cloud infrastructure. It has brought the benefits of software engineering, including version control, code review, testing, and automation, to infrastructure management. But it has also brought the risks of software engineering, including bugs, technical debt, and security vulnerabilities, to a domain where the blast radius of a mistake can be an entire cloud account.

The misconfigurations examined in this article are not edge cases. They are the patterns that appear in real codebases every day, written by experienced engineers under time pressure, generated by AI coding assistants optimizing for functionality over security, and inherited from public modules with different security assumptions.

Catching these misconfigurations before they reach production requires a layered approach: static analysis of IaC files, plan-time analysis of intended changes, policy-as-code enforcement, and runtime posture assessment. It requires integration into the developer's workflow at the IDE level and enforcement in the CI/CD pipeline. And it requires treating IaC security not as a one-time audit activity but as a continuous process that evolves with the infrastructure it protects.

The organizations that get this right will have infrastructure that is not only reproducible and version-controlled but also provably secure. The organizations that do not will continue to learn about their misconfigurations the hard way: from incident reports.

Secure your AI-generated code with SafeWeave

8 security scanners running in parallel, right inside your AI editor. SAST, secrets, dependencies, IaC, containers, DAST, license compliance, and security posture — all in one command.

No credit card required · 3 scanners free forever · Runs locally on your machine