What is infrastructure as code?
Infrastructure as code (IaC) is the practice of defining and managing cloud infrastructure through machine-readable configuration files rather than manual console clicks. This shift enables DevSecOps teams to manage networks, IAM roles, and databases using the same version-controlled workflows as application code.
Treating infrastructure like application code lets you apply software engineering best practices in the cloud. This includes version control with Git, peer reviews for pull requests, automated testing, and CI/CD integration.
For DevSecOps teams, IaC forms the foundation for security as code. When you define networks, IAM roles, and databases in text files, you can scan for misconfigurations and prevent risky deployments before they occur.
Get the IaC Best Practices [Cheat Sheet]
Stop repeat misconfigurations before they hit production. Download our IaC Best Practices Cheat Sheet to learn how to integrate scanning early, fix at the source, and enforce policies consistently.

How infrastructure as code works
IaC does more than create basic virtual machines. It manages the entire stack. You can use it for low-level infrastructure such as VPCs, subnets, and routing tables; middleware such as load balancers and message queues; and high-level services such as Kubernetes manifests and Lambda functions.
Here’s the typical IaC workflow:
A developer writes configuration scripts in JSON, YAML, or HCL.
The code goes into a Git repository.
When a pull request is merged, it triggers a CI/CD pipeline.
The pipeline sends the code to an IaC tool like Terraform, which converts it into API calls for your cloud provider.
How does the tool know what’s already in your cloud and what still needs to be created?
That’s where the state file comes in.
The state file is a large JSON map. It records the relationship between your configuration and the actual resources in your cloud. When you change your code, your IaC tool compares it to the state file, finds what changed, and runs only the API calls needed to match your code with reality.
What are the security benefits of IaC?
Immutable infrastructure
In the past, vulnerable servers were usually patched in place. Over time, those one-off fixes created drift. IaC solves this problem by promoting immutable infrastructure. If a server needs a patch, you update the base image in your code, remove the old server, and create a new, fully patched one.
Accelerated disaster recovery
With IaC, if someone breaches your environment and compromises your instances, you don’t have to rely on manual cleanup. Since your entire infrastructure is defined in code, you can remove the compromised environment, quickly create a clean copy in another region, and reroute your traffic.
Enforced standardization
Automated provisioning removes common human errors. With IaC, every resource follows your organization's security standards. For example, if your policy requires all databases to be encrypted at rest, your IaC templates ensure this is enforced consistently.
Built-in audit trail for compliance
Because all infrastructure changes flow through Git, every modification is automatically tracked, who made the change, what was changed, when it happened, why (via commit messages), and who approved it (via pull request reviews). This creates a version-controlled audit trail that satisfies compliance requirements under frameworks like SOC 2, ISO 27001, and PCI DSS without maintaining separate documentation. For GRC teams, this means audit evidence is generated as a natural byproduct of the engineering workflow, not a manual retrospective exercise.
How do you write IaC?
There are two main approaches to writing infrastructure as code: declarative and imperative.
Declarative (functional)
In a declarative approach, you define the end state you want. For example, your code might say, "I want a VPC with three subnets, two EC2 instances, and an S3 bucket." You do not tell the tool how to build them. The tool checks the current state, determines the required API calls, and creates the resources. Terraform, Kubernetes, and CloudFormation use this approach.
Imperative (procedural)
In an imperative approach, you give step-by-step instructions to reach a result. For example, your script might say:
Create a VPC.
If VPC creation succeeds, create subnet A.
Boot an EC2 instance.
Here, you define the steps, not just the end state. Ansible, Chef, and custom bash scripts use this approach.
DevSecOps teams usually prefer declarative tools. Declarative code serves as a single source of truth for the environment's expected state. This makes it easy to spot drift. If someone changes a setting manually in the cloud console, the tool quickly sees that the real environment doesn’t match what is in Git.
Popular infrastructure-as-code tools and technologies
The IaC ecosystem is broad, but only a few tools are widely used:
Terraform / OpenTofu (open-source Terraform): The industry standard for infrastructure as code. Terrraform supports multiple environments through a single workflow. This consistency allows DevSecOps teams to provision resources across AWS, GCP, and Azure, while also configuring SaaS platforms like GitHub or Datadog using the same definition files. Many teams use Terraform for multi-cloud environments, and if you’re new to infrastructure as code, Terraform is a great place to start.
Ansible: While Terraform is good for provisioning infrastructure, Ansible excels at configuration management, such as installing software and managing operating system settings on servers.
Pulumi: Pulumi takes a different approach by letting teams write IaC in general-purpose programming languages rather than domain-specific languages like HCL. This makes it appealing to engineering teams that want the full power of a real programming language (loops, conditionals, abstractions, unit testing) without learning new syntax. Pulumi supports multi-cloud environments and manages state through its managed service or self-hosted backends.
Kubernetes (Helm/Kustomize): In containerized environments, Kubernetes manifests (written in YAML) define how your microservices run, scale, and communicate. Helm and Kustomize help package and template these complex configurations.
Cloud-native tools: Each major cloud provider offers its own tools. AWS has CloudFormation, Google has Cloud Deployment Manager, and Resource Manager (ARM) templates or Bicep are used for infrastructure-as-code Azure deployments. Enterprise teams working within a single cloud provider often rely on these native tools.
IaC security scanners: Tools such as Checkov, tfsec, and Terrascan analyze Terraform, CloudFormation, and Kubernetes manifests for misconfigurations before deployment. They help teams catch issues like public storage buckets or overly permissive IAM policies early in the development lifecycle.
Watch 5-minute demo
Watch the demo to learn how Wiz Code scans infrastructure as code, container images, and CI/CD pipelines to catch risks early—before they reach the cloud.

What are the challenges of infrastructure as code?
IaC brings enormous benefits, but it also introduces risks that scale at the same speed as your deployments. A misconfigured module doesn't create one vulnerable resource. It creates hundreds across every environment that consumes it. These are the challenges DevSecOps teams encounter most frequently:
State file exposure
State files often store resource IDs, database connection strings, access tokens, and sometimes root credentials in plain text. If an attacker obtains your terraform.tfstate file, they gain a complete map of your infrastructure (every resource name, every IP address, every relationship) and potentially direct access to sensitive resources. State files are one of the highest-value targets in any IaC-managed environment.
Module inconsistency
Without standardized templates, engineers working under time pressure create one-off configurations with inconsistent IAM policies, network rules, security group settings, and encryption standards across environments. What starts as a quick fix in dev becomes a pattern that propagates to staging and production. Over time, the same resource type is configured differently across dozens of deployments, making it nearly impossible to assess your actual security posture.
Configuration drift
Drift happens when someone bypasses the IaC pipeline and makes a manual change directly in the cloud console. Opening port 22 to debug an issue, widening an IAM policy to unblock a deployment, disabling encryption to troubleshoot a performance problem. These changes don't exist in Git, aren't tracked in the state file until the next apply, and often persist long after the original issue is resolved. Drift creates shadow infrastructure that your security tooling doesn't know about.
Tagging gaps
If resources aren't tagged consistently, you can't track ownership, allocate costs, group resources for dynamic security policies, or identify orphaned infrastructure. In incident response scenarios, untagged resources slow down triage because nobody can determine who deployed them, which team owns them, or whether they're production or test.
Secrets in code and state
Hardcoded API keys, database passwords, and service account tokens in IaC templates and variable files are one of the most common and most dangerous IaC security failures. Once committed to Git, secrets persist in repository history even after the file is modified or deleted. They're also discoverable by adversaries through OSINT techniques like public repository scanning with tools such as TruffleHog or Gitleaks. State files compound the problem: Terraform state can contain secrets in plain text if resources like database passwords are managed directly in the configuration.
Lack of runtime context in scanning
Traditional IaC scanners evaluate templates in isolation. They can tell you that an S3 bucket is configured without encryption, but they can't tell you whether that bucket contains sensitive data, is publicly accessible, or is connected to an over-privileged IAM role that creates a real attack path. Without runtime context, teams either treat every finding as critical (alert fatigue) or deprioritize everything (accumulated risk).
Infrastructure as code best practices
Each of these practices directly addresses one or more of the challenges above. DevSecOps teams that implement all six create a layered defense where IaC security is enforced at every stage, from the developer's IDE through the pipeline to the live environment.
Secure state management
Never commit state files to Git. Store them in encrypted, remote backends with strict access controls.
For Terraform, use an S3 bucket (or equivalent cloud storage) with server-side encryption via KMS, versioning enabled for rollback capability, and bucket policies that restrict access to only the CI/CD pipeline service account and a small set of authorized operators. Enable state locking via a DynamoDB table (AWS) or equivalent mechanism so that two pipelines can't write to the state concurrently and corrupt your infrastructure.
Audit access to state backends with the same rigor you'd apply to a production database, because that's effectively what they are.
Module standardization with golden templates
Security and platform teams should create pre-approved, hardened IaC modules for the resource patterns engineers use most frequently. VPCs with standardized network segmentation. S3 buckets with encryption and access logging enabled by default. IAM roles scoped to least privilege. These "golden templates" serve as secure building blocks that developers consume rather than writing configurations from scratch.
The leverage is in maintenance: when a security standard changes (a new encryption requirement, a logging policy update), you update the module once and the fix propagates to every deployment that uses it at the next apply. This turns security updates from a per-resource manual effort into a single code change.
Automated drift reconciliation
Detecting drift is the minimum. Alerting on drift is better. But the goal is automated reconciliation, a true GitOps model where your pipelines continuously compare the live environment to the Git source of truth and automatically roll back unauthorized manual changes.
Implement scheduled reconciliation runs (not just on-deploy checks) that compare live infrastructure state against the declared IaC configuration. For changes that can be safely auto-reverted (like a security group opened to 0.0.0.0/0), automate the rollback. For changes that require judgment (like a resource scaling adjustment made during an incident), generate a ticket with full context for the owning team to review.
Tagging governance
Enforce tagging at the pipeline level, not through documentation or team agreements. Use policy-as-code tools like Open Policy Agent (OPA) or HashiCorp Sentinel to evaluate every terraform plan and reject any build that attempts to create a resource without required tags: owner, team, environment, cost center, data classification.
Tagging should be defined in your golden templates so developers don't have to remember which tags to apply. If a resource can't be created without proper tags, orphaned and untracked infrastructure stops being a problem.
Secrets management
Never store secrets directly in IaC files, variable definitions, .tfvars files, or environment variables committed to repositories. Instead, integrate with external secrets management tools: HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, or Google Cloud Secret Manager. Your IaC code references a secret's location (a Vault path or Secrets Manager ARN), and the actual value is injected dynamically at runtime.
Run secret scanning in two places: in the developer's IDE (catching accidental hardcoding before commit) and in the CI/CD pipeline (as a gate that blocks merges containing detected secrets). Tools like Wiz Code, TruffleHog, and Gitleaks scan for patterns matching API keys, private keys, database connection strings, and tokens.
Treat any secret that has ever been committed to a repository, even if immediately removed, as compromised. Rotate it immediately. Git history is permanent, and adversaries scan public repositories continuously.
Pipeline-integrated policy as code
Embed organizational guardrails directly into the IaC pipeline so non-compliant infrastructure is blocked at the terraform plan stage, not flagged after deployment. Policy-as-code frameworks evaluate every execution plan against codified rules before any changes are applied to the environment.
The most widely used frameworks include OPA/Rego (cloud-agnostic, open source), HashiCorp Sentinel (integrated with Terraform Cloud/Enterprise), and cloud-native options like AWS Config Rules and Azure Policy. Typical rules include: all storage must be encrypted at rest, no security group may allow unrestricted inbound access, all databases must be in private subnets, and all resources must include required tags.
The developer experience matters here. Policy violations should surface as clear, actionable feedback in the pull request, not a cryptic pipeline failure. When a developer sees "this S3 bucket is missing server-side encryption, add server_side_encryption_configuration to fix" directly in their PR, they fix it in minutes. When they see "pipeline failed: policy check error," they open a support ticket and wait.
How Wiz secures infrastructure as code across development lifecycles
Wiz Code connects your static Terraform templates directly to your live cloud environment. It maps these relationships on the Wiz Security Graph to show you exactly how a misconfiguration impacts your security. This correlation reduces alert noise by 90% and enables DevSecOps teams to identify the root cause of a misconfiguration in minutes rather than days.
Here’s how Wiz changes IaC security from a system full of alerts to a proactive prevention tool:
IDE-to-pipeline scanning: Security testing should fit naturally into the developer workflow. Wiz Code scans Terraform, CloudFormation, and Kubernetes manifests directly in the developer's IDE and continues scanning in CI/CD pipelines, checking for known misconfigurations before changes are merged or deployed. It finds syntax errors and clear security flaws, such as a public-facing storage bucket, before the developer commits the code and again during pipeline runs to prevent risky infrastructure from reaching production.
Code-to-cloud correlation: This is where Wiz stands out. Traditional scanners only look at a Terraform file by itself. Wiz uses the Wiz Security Graph to automatically map running cloud resources back to the exact source IaC files and the developers who created them. If a runtime alert happens for a risky workload, your security team doesn’t have to guess who deployed it. And there's no more guesswork about what went wrong, either: Wiz provides instant root-cause analysis that links directly to the Git repository.
Contextual prioritization: Not all misconfigurations are equally important. An unencrypted database in an isolated sandbox is a low priority, but an unencrypted database with internet access and admin privileges is an emergency. Wiz’s attack path analysis adds real runtime context to your IaC findings. It filters out less important issues and prioritizes misconfigurations that could lead to real attack paths to sensitive data.
Drift detection: Wiz constantly compares your live cloud resources to their original IaC definitions. If someone makes a manual change that weakens your infrastructure, such as modifying a security group to 0.0.0.0/0, Wiz immediately flags the configuration drift so you can fix it.
AI-assisted remediation: Wiz provides developers with AI-generated code fixes for IaC misconfigurations, delivered directly into their normal workflow as pull request comments.
Secret scanning: Wiz proactively finds hardcoded API keys, passwords, private keys, and tokens in your IaC files and application code before they’re committed, stopping one of the most critical attack risks at the source.
Ready to stop chasing contextless alerts? Schedule a demo today to see how Wiz Code identifies 1,400+ misconfigurations in your IDE and maps them to real attack paths on the Wiz Security Graph.
Get a Wiz Code demo
See how Wiz Code identifies misconfigurations in your IaC templates and maps them to real attack paths on the Security Graph
