Blog
AppSec Blog

IaC security checklist: Prevent cloud misconfigurations before deployment

 - 
October 2, 2026

Infrastructure as code (IaC) security scanners like Checkov, Trivy, KICS, and Invicti IaC Security automate the detection of infrastructure misconfigurations. But knowing what they check for, why each control matters, and which issues should block deployment is what makes IaC security reviews useful rather than mechanical.

‍

This checklist organizes the most important infrastructure as code security controls by domain. Use it for pull request reviews, continuous integration and continuous delivery (CI/CD) policy configuration, security audits, and onboarding new engineering teams to secure IaC practices.

You information will be kept Private
Table of Contents

The OWASP Infrastructure as Code Security Cheat Sheet recommends integrating IaC security into the development lifecycle through integrated development environment (IDE) plugins, secrets management, least privilege, static analysis, CI/CD checks, artifact signing, logging, monitoring, and runtime detection. This article turns those principles into a practical, copy-ready checklist for DevSecOps teams.

What is an IaC security checklist?

An IaC security checklist is a domain-organized reference of infrastructure security controls that should be validated in Terraform, CloudFormation, Kubernetes, Helm, Bicep, Azure Resource Manager (ARM) templates, Ansible, Pulumi, Dockerfiles, and other infrastructure-as-code configuration files.

Teams use IaC security checklists during code review, automated scanner configuration, and security audits to ensure infrastructure configuration meets baseline security requirements before deployment.

How to use this checklist

In pull request review, use the checklist as a reference when reviewing infrastructure changes, especially complex identity and access management (IAM) policies, security group rules, Kubernetes pod security contexts, and Terraform modules.

In CI/CD scanner configuration, use it as a baseline for Checkov policies, Trivy checks, KICS queries, custom Open Policy Agent (OPA) Rego rules, and deployment gates.

In security audits, use it to document which controls are implemented, which are enforced automatically, which require manual review, and which exceptions have been formally accepted.

The severity levels in this checklist are recommended starting points for deployment policy, not universal vulnerability severity ratings. Adjust them for asset criticality, exposure, compensating controls, and your organization’s risk tolerance:

  • Critical: Block deployment. Reserve this level for directly exploitable exposure and credential compromise.
  • High: Block deployment or require documented risk acceptance.
  • Medium: Warn, track, and remediate within the agreed service level agreement (SLA).
  • Low: Treat as informational hardening guidance.

What IaC scanning can and can’t enforce

Static IaC scanning reads infrastructure definitions. It can check what a Terraform file, CloudFormation template, or Kubernetes manifest says, but it can’t see repository settings, the state of a live cloud account, or behavior at runtime.

That split matters when you assign owners. Most items listed below are resource-level settings that a scanner can check in code. A few need another control: branch protection and pull request rules live in repository settings, root account keys and multi-factor authentication (MFA) live in account configuration, and drift detection compares deployed resources with code. Items in this checklist that an IaC scanner can’t verify are marked with a note on where to enforce them.

Domain 1: Identity and access management

IAM misconfigurations create privilege escalation paths. An overly permissive role attached to a compromised Lambda function, EC2 instance, CI/CD workflow, or Kubernetes service account can give attackers broad access across the cloud account. Least privilege should be encoded in IaC, not left to manual review after deployment.

IAM checklist

Check Severity Control Implementation note
☐ Critical No IAM policies combine a wildcard Action with a wildcard Resource. Treat service-wide actions such as iam:* on Resource: "*" the same way.
☐ Critical No root account access keys are created or used. Account-level control – verify through account settings and cloud benchmarks because IaC scanning can’t see account state.
☐ High IAM role trust policies are scoped to specific principals. Avoid unnecessarily broad trust relationships.
☐ High Avoid Principal: "*" in trust policies unless explicitly required and risk accepted. In Terraform, this appears as identifiers = ["*"] under principals.
☐ High Cross-account and third-party trust policies include conditions. Examples include sts:ExternalId for third parties and aws:PrincipalOrgID for access within your organization.
☐ High EC2 instances require the Instance Metadata Service version 2 (IMDSv2). Set http_tokens = "required" in metadata_options.
☐ High Human access uses federated single sign-on with MFA instead of long-lived IAM users. Where passwords remain as a single authentication factor, use a minimum length of at least 15 characters in line with current NIST SP 800-63B guidance.
☐ Medium IAM policies are attached to roles or groups, not directly to users. Managed policies are preferred over inline policies so they can be reused and reviewed consistently.
☐ Medium Service accounts use dedicated IAM roles. Avoid shared workload roles.
☐ Medium Lambda, EC2, ECS, and CI/CD execution roles do not have admin-level permissions. Avoid permissions such as AdministratorAccess.
☐ Medium IAM Access Analyzer is enabled. Use it to flag unintended external access.
☐ Low Permission boundaries are defined for delegated administrator use cases. Use them to constrain delegated permissions where appropriate.

The following Terraform policy statement shows the difference between unrestricted permissions and a role scoped to specific S3 actions and resources:

# Vulnerable
statement {
  effect    = "Allow"
  actions   = ["*"]
  resources = ["*"]
}

# Better
statement {
  effect    = "Allow"
  actions   = ["s3:GetObject", "s3:PutObject"]
  resources = ["arn:aws:s3:::my-specific-bucket/*"]
}

EC2 metadata configuration is another important IAM-adjacent control. Requiring IMDSv2 ensures that applications must use the token-based metadata service rather than allowing direct IMDSv1 requests:

# Vulnerable: metadata_options omitted, so IMDSv1 may still be allowed
resource "aws_instance" "example" {
  # ...
}

# Better
resource "aws_instance" "example" {
  # ...
  metadata_options {
    http_endpoint = "enabled"
    http_tokens   = "required"
  }
}

IMDSv2 matters for application security as much as for infrastructure. With IMDSv1, some server-side request forgery (SSRF) flaws can retrieve instance role credentials with a simple metadata request. IMDSv2 requires a session-token exchange that blocks many straightforward SSRF techniques that can retrieve credentials from IMDSv1.

A note on password policy: NIST SP 800-63B discourages mandatory periodic rotation and composition rules, and favors length and screening against known-compromised passwords. Don’t build a policy gate around forced rotation.

The Checkov policy index covers IAM, cloud, Kubernetes, Dockerfile, secrets, CI/CD, and other IaC checks, which can be used to map these controls into scanner enforcement.

Domain 2: Network security

Network misconfiguration is one of the most visible cloud attack surfaces. Public security groups, exposed databases, permissive firewall rules, and publicly accessible storage can turn an internal system into an internet-facing target.

Network security checklist

Check Severity Control Implementation note
☐ Critical No security groups allow ingress from 0.0.0.0/0 or ::/0 on administrative or database ports. Examples include SSH on 22, RDP on 3389, 1433, 3306, 5432, 6379, and 27017.
☐ Critical No public S3 bucket ACLs or public bucket policies. Disable ACLs with bucket owner enforced object ownership, and block public access explicitly.
☐ Critical RDS, ElastiCache, and Redshift resources are not publicly accessible unless explicitly approved. Restrict database and data-store access to required private networks and services.
☐ High Security groups use specific CIDR blocks rather than broad ranges. A /8 or /16 block is rarely specific enough, and rules shouldn’t open all ports or all protocols to outside sources.
☐ High Load balancers use HTTPS listeners with a TLS policy that sets TLS 1.2 as the minimum. Apply an appropriate current TLS security policy.
☐ High HTTP listeners redirect to HTTPS. Avoid serving application traffic over unencrypted HTTP.
☐ High Amazon EKS API server endpoint access is private or restricted to approved CIDRs. The public endpoint is open by default.
☐ Medium CloudFront distributions enforce HTTPS. Redirect or reject HTTP requests as appropriate.
☐ Medium The default security group in each VPC restricts all inbound and outbound traffic. Define explicit security groups for required connectivity.

For network access, avoid opening administrative ports to the entire internet. This example restricts SSH access to a specific trusted address instead:

# Vulnerable
ingress {
  from_port   = 22
  to_port     = 22
  protocol    = "tcp"
  cidr_blocks = ["0.0.0.0/0"]
}

# Better
ingress {
  from_port   = 22
  to_port     = 22
  protocol    = "tcp"
  cidr_blocks = ["203.0.113.10/32"] # bastion or VPN egress address
}

Better still, remove inbound SSH altogether and use AWS Systems Manager Session Manager for shell access:

resource "aws_s3_bucket_ownership_controls" "example" {
  bucket = aws_s3_bucket.example.id

  rule {
    object_ownership = "BucketOwnerEnforced"
  }
}

resource "aws_s3_bucket_public_access_block" "example" {
  bucket                  = aws_s3_bucket.example.id
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

VPC Flow Logs are covered in Domain 6, where network activity monitoring belongs alongside other logging and audit controls.

Domain 3: Encryption

Encryption controls reduce the impact of unauthorized access and support compliance expectations across frameworks such as Payment Card Industry Data Security Standard (PCI DSS), Health Insurance Portability and Accountability Act (HIPAA), SOC 2, ISO 27001, and General Data Protection Regulation (GDPR).

Encryption checklist

Check Severity Control Implementation note
☐ Critical RDS storage encryption is enabled. Existing unencrypted instances can’t be encrypted in place, so plan a snapshot-copy-and-restore migration.
☐ Critical EBS volumes are encrypted. Consider enabling EBS encryption by default at the account level as well.
☐ High S3 buckets use server-side encryption with a customer-managed KMS key where policy requires key-level control. New S3 objects get SSE-S3 encryption by default, so the check that adds value is who controls the key.
☐ High KMS key rotation is enabled. Set enable_key_rotation = true.
☐ High CloudTrail logs are encrypted with KMS. Protect audit data with appropriate key access controls.
☐ High ElastiCache encryption at rest and in transit is enabled. Protect both stored data and network traffic.
☐ Medium SNS topics are encrypted with KMS. Apply customer-managed keys where policy requires key-level control.
☐ Medium SQS queues are encrypted at rest. New queues get SSE-SQS by default, but set encryption explicitly or use KMS where required.

RDS encryption should be explicit in the resource definition. Where key-level control is required, specify the KMS key used to encrypt the database:

resource "aws_db_instance" "example" {
  storage_encrypted = true
  kms_key_id        = aws_kms_key.rds.arn
}

S3 follows the same principle, but the configuration is defined separately from the bucket resource. The following example uses a customer-managed KMS key and enables S3 Bucket Keys:

resource "aws_s3_bucket_server_side_encryption_configuration" "example" {
  bucket = aws_s3_bucket.example.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm     = "aws:kms"
      kms_master_key_id = aws_kms_key.mykey.arn
    }
    bucket_key_enabled = true
  }
}

Domain 4: Secrets and credential management

Hardcoded credentials in IaC files are high-risk because they are committed to version control history. Removing the current value from the latest commit does not remove it from prior commits, forks, logs, or CI/CD artifacts.

Secrets checklist

Check Severity Control Implementation note
☐ Critical No hardcoded passwords, API keys, tokens, private keys, or cloud access keys in IaC files. Use external secrets managers or service-managed credentials instead.
☐ Critical No plaintext secrets are set as environment variable values in Lambda, ECS, Kubernetes, or CI/CD definitions. Reference a secrets manager or a Kubernetes Secret instead of inline values.
☐ High Kubernetes Secrets are encrypted at rest. For managed clusters, use envelope encryption with a KMS key, for example encryption_config on aws_eks_cluster.
☐ High AWS Secrets Manager secrets have rotation configured. Define rotation according to the credential and service requirements.
☐ Medium .tfvars files containing sensitive values, local state files, and .terraform/ directories are excluded from Git. Include *.tfstate and other local Terraform artifacts in repository exclusions.
☐ Medium Terraform variables containing secrets are marked sensitive = true. This redacts values in CLI output only. It does not keep them out of state.
☐ Low Secrets scanning runs as a pre-commit hook and in CI/CD. Scan before secrets can enter shared repository history.

The safest pattern is to avoid passing the database password through Terraform at all when the service can manage it directly. This example replaces a hardcoded password with an RDS-managed secret:

# Vulnerable
resource "aws_db_instance" "example" {
  password = "SuperSecretPassword123!"
}

# Better: RDS generates and manages the password in Secrets Manager
resource "aws_db_instance" "example" {
  manage_master_user_password   = true
  master_user_secret_kms_key_id = aws_kms_key.rds.arn
}

Avoid reading a secret with the aws_secretsmanager_secret_version data source and passing it to a resource argument. Terraform writes the resolved value to state in plaintext. Recent Terraform and AWS provider versions support ephemeral resources and write-only arguments that can keep supported values out of state and plan files, so check the provider documentation for the resources you use.

Terraform state protection is covered in Domain 7.

Domain 5: Kubernetes pod security

Kubernetes pod security misconfigurations can create privilege escalation and container escape paths. Official Kubernetes Pod Security Standards define privileged, baseline, and restricted profiles for pod-level controls.

Two of those profiles map directly onto this checklist. The baseline profile covers privileged containers, host namespaces, and hostPath volumes. The restricted profile adds running as non-root, dropping all capabilities, blocking privilege escalation, and the RuntimeDefault seccomp profile. Setting readOnlyRootFilesystem is good practice, but it is not a Pod Security Standards requirement.

Kubernetes checklist

Check Severity Control Implementation note
☐ Critical No containers run as privileged. Set privileged: false.
☐ Critical Containers do not run as root. Set runAsNonRoot at the pod or container level.
☐ Critical allowPrivilegeEscalation is set to false. Prevent processes from gaining more privileges than their parent process.
☐ High Pod Security Admission applies an appropriate Pod Security Standard through namespace labels. Use the restricted profile where workload compatibility allows.
☐ High RBAC grants no cluster-admin bindings to workloads and Roles avoid wildcard verbs and resources. Scope Kubernetes permissions to workload requirements.
☐ High readOnlyRootFilesystem is set to true where possible. Provide writable volumes only where the workload needs them.
☐ High CPU and memory requests and limits are defined. Set resource constraints appropriate to each workload.
☐ High HostPath volumes are not used unless explicitly required and tightly scoped. Avoid exposing host filesystem paths to containers.
☐ High hostNetwork, hostPID, and hostIPC are false, and hostPort is not used. Avoid unnecessary access to host namespaces and ports.
☐ High NetworkPolicies are defined for all namespaces. Prefer starting with default deny and explicitly allowing required traffic.
☐ High Linux capabilities are dropped by default. Add back only capabilities the workload requires.
☐ High Pods use the RuntimeDefault seccomp profile. Apply the runtime’s default syscall filtering profile.
☐ Medium Service accounts set automountServiceAccountToken: false unless Kubernetes API access is required. Avoid unnecessarily exposing API credentials to workloads.
☐ Medium Container images are not referenced by the latest tag. Use pinned versions or digests.

Kubernetes security contexts make many of these controls explicit in the workload manifest. Start by disabling privileged execution and privilege escalation:

# Vulnerable
securityContext:
  privileged: true

# Better
securityContext:
  privileged: false
  allowPrivilegeEscalation: false

Running as non-root adds another layer of isolation. Set runAsNonRoot and, where appropriate, specify a non-zero user ID:

securityContext:
  runAsNonRoot: true
  runAsUser: 1000

Rather than relying only on workload-by-workload settings, Pod Security Admission can enforce a security profile across an entire namespace. This example applies the restricted profile to the payments namespace:

apiVersion: v1
kind: Namespace
metadata:
  name: payments
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/enforce-version: latest

At the container level, drop Linux capabilities by default and add back only the specific capabilities the application requires:

securityContext:
  capabilities:
    drop:
      - ALL
    add:
      - NET_BIND_SERVICE

Finally, apply the runtime’s default seccomp profile to restrict the system calls available to the container:

securityContext:
  seccompProfile:
    type: RuntimeDefault

Together, these settings implement several of the workload-level controls required or recommended by the restricted Pod Security Standard. 

Domain 6: Logging, monitoring, and audit trails

Security controls are much less useful if activity cannot be reviewed. Logs and audit trails are essential for detection, investigation, compliance evidence, and incident response.

Logging checklist

Check Severity Control Implementation note
☐ Critical CloudTrail is enabled in all regions. Set is_multi_region_trail = true.
☐ High CloudTrail log file validation is enabled. Set enable_log_file_validation = true.
☐ High CloudTrail logs are stored in a protected S3 bucket. Restrict access to audit data.
☐ High CloudTrail records S3 data events for sensitive buckets. Include object-level activity where required.
☐ High VPC Flow Logs are enabled. Capture network flow metadata for monitoring and investigation.
☐ High RDS logging is enabled where supported. Enable appropriate database log exports.
☐ High Kubernetes audit logging is enabled. For EKS, include audit in enabled_cluster_log_types.
☐ Medium GuardDuty or equivalent threat detection is enabled across accounts and regions. Apply threat detection consistently across the environment.
☐ Medium AWS Config and Security Hub are enabled. Use them to provide continuous evidence of configuration state after deployment.
☐ Medium CloudWatch alarms exist for security-relevant events. Cover root account usage, unauthorized API calls, MFA disable events, policy changes, and suspicious access patterns.
☐ Low Logs are retained according to security and compliance requirements. Define retention periods based on organizational and regulatory requirements.

A baseline CloudTrail configuration should cover all regions, validate log integrity, and encrypt the resulting audit data. The following Terraform resource enables all three controls:

resource "aws_cloudtrail" "main" {
  name                       = "org-trail"
  s3_bucket_name             = aws_s3_bucket.trail.id
  is_multi_region_trail      = true
  enable_log_file_validation = true
  kms_key_id                 = aws_kms_key.trail.arn
}

Domain 7: CI/CD pipeline and Terraform state security

CI/CD pipelines and Terraform state are part of the infrastructure attack surface. A compromised workflow can deploy malicious infrastructure. A leaked state file can expose secrets, resource identifiers, and sensitive configuration. For deeper pipeline guidance, see the OWASP CI/CD Security and GitHub Actions Security cheat sheets.

CI/CD and state checklist

Check Severity Control Implementation note
☐ Critical Terraform state is stored in an encrypted remote backend, not local state. Protect state as sensitive infrastructure data.
☐ High Terraform state locking is enabled. Native S3 locking with use_lockfile requires Terraform 1.10 or later.
☐ High CI/CD actions are pinned to full-length commit SHAs, not floating references or mutable tags. Record the corresponding release in a comment so the immutable reference remains understandable.
☐ High Pipelines authenticate to cloud accounts with short-lived credentials through OIDC federation instead of long-lived access keys stored as secrets. Scope the cloud-side trust policy to the specific repository and branch or environment, not a wildcard.
☐ High CI/CD tokens use least privilege. For GitHub Actions, default workflow permissions to read-only and widen them per job.
☐ High Workflows do not interpolate untrusted input directly into shell commands. Treat values such as pull request titles and branch names as untrusted, and use pull_request_target only with care.
☐ High The Terraform state bucket has versioning enabled, blocks public access, limits access to the pipeline role, and logs access. Apply defense in depth to the state backend.
☐ High Infrastructure changes require pull request review before merge. Enforce in repository settings, not through IaC scanning.
☐ High Direct pushes to protected branches are blocked. Enforce in repository settings.
☐ High Separate backends, ideally separate accounts, are used for dev, staging, and production. Workspaces within one backend share credentials and don’t provide strong isolation.
☐ Medium terraform plan output is reviewed before apply, and apply runs the reviewed saved plan. Plan files can contain secrets, so store them as protected artifacts.
☐ Medium Policy-as-code checks run before deployment. Evaluate infrastructure policy before changes are applied.
☐ Medium Production applies require approval through protected environments. Use deployment controls appropriate to production risk.
☐ Low Automated drift detection flags manual production changes. Drift detection compares deployed resources with code, so it needs cloud-side or pipeline tooling rather than static scanning.

A production Terraform backend should combine remote state, encryption, and state locking. This S3 backend example also uses a customer-managed KMS key for key-level control:

terraform {
  backend "s3" {
    bucket       = "my-terraform-state"
    key          = "prod/terraform.tfstate"
    region       = "us-east-1"
    encrypt      = true
    kms_key_id   = "arn:aws:kms:us-east-1:111122223333:key/EXAMPLE"
    use_lockfile = true
  }
}

Native S3 locking with use_lockfile requires Terraform 1.10 or later. HashiCorp has deprecated the older dynamodb_table locking argument, so teams on earlier versions should plan an upgrade. Note that encrypt = true alone applies S3-managed encryption. Add kms_key_id if you need key-level control.

GitHub Actions references need similar supply chain discipline. A branch or release tag can be moved, while a full commit SHA identifies one immutable revision of the action:

# Risky
uses: actions/checkout@main

# Also risky: tags can be moved to point at different code
uses: actions/checkout@v7

# Better: pin to the full commit SHA and note the release in a comment
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

Tags are mutable. In March 2025, attackers compromised the tj-actions/changed-files action and repointed its tags to malicious code that exposed secrets from the workflows using it. A full commit SHA is the only reference that can’t be silently changed.

Module and provider pinning are covered in Domain 8.

Domain 8: Supply chain and module integrity

IaC depends on external modules, providers, actions, Helm charts, and container images. Those dependencies extend the software supply chain beyond the infrastructure code stored in your own repository. Pinning, reviewing, and verifying external artifacts reduces the risk of an upstream change silently altering what you deploy.

Supply chain checklist

Check Severity Control Implementation note
☐ High Terraform modules are pinned to specific versions. For Git sources, pin to a commit SHA with ?ref=.
☐ High The Terraform dependency lock file (.terraform.lock.hcl) is committed. It tracks provider selections and checksums, not remote module versions, so modules still need explicit version constraints or immutable Git references.
☐ High Helm chart dependencies are pinned in Chart.lock, with exact versions in Chart.yaml. Keep dependency resolution reproducible.
☐ High Container base images are pinned to immutable digests. Replace mutable image references with a vetted digest.
☐ Medium Provider version constraints are defined in required_providers. Constrain provider upgrades to reviewed versions.
☐ Medium Public Terraform modules are reviewed before adoption. Review infrastructure behavior and security implications before use.
☐ Medium Public modules are checked for recent maintenance, open issues, and trusted ownership. Include project health and provenance in dependency review.
☐ Medium Infrastructure artifacts are signed where possible. Examples include container images with Sigstore cosign and Helm charts with provenance files. Terraform verifies provider signatures, but registry modules carry no equivalent, so pinning and review matter more. Signing happens in the build pipeline, not in IaC scanning.
☐ Low Software bills of materials (SBOMs) are generated for infrastructure and container artifacts. Use SBOMs to improve dependency inventory and supply chain visibility.

For registry-hosted Terraform modules, specify an exact module version rather than allowing an unconstrained upgrade:

module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.1.2"
}

Container images need the same treatment. Tags such as node:20-alpine can resolve to different image contents over time, while a digest pins the build to a specific image:

# Risky
FROM node:20-alpine

# Better (replace the placeholder with the digest of the image you've vetted)
FROM node:20-alpine@sha256:<image-digest>

Beyond Terraform: CloudFormation, Azure, and Google Cloud

The controls above apply regardless of IaC tool or cloud platform. The syntax and services change, but the underlying security requirements remain largely the same.

CloudFormation equivalents

The same secrets, encryption, and exposure rules apply to AWS CloudFormation templates. The examples below show the “better” form of two common controls:

# Secrets: let RDS manage the master password instead of hardcoding it
Database:
  Type: AWS::RDS::DBInstance
  Properties:
    StorageEncrypted: true
    PubliclyAccessible: false
    ManageMasterUserPassword: true
    # ...

# S3: KMS encryption, public access blocked, ACLs disabled
ExampleBucket:
  Type: AWS::S3::Bucket
  Properties:
    BucketEncryption:
      ServerSideEncryptionConfiguration:
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: aws:kms
            KMSMasterKeyID: !GetAtt BucketKey.Arn
          BucketKeyEnabled: true
    PublicAccessBlockConfiguration:
      BlockPublicAcls: true
      BlockPublicPolicy: true
      IgnorePublicAcls: true
      RestrictPublicBuckets: true
    OwnershipControls:
      Rules:
        - ObjectOwnership: BucketOwnerEnforced

CloudFormation also adds a few template-specific checks:

Check Severity Control Implementation note
☐ Critical Sensitive parameters are never given plaintext defaults. Use dynamic references such as {{resolve:secretsmanager:...}} to fetch secrets at deploy time. NoEcho masks a parameter in console and API output, but it is not a substitute for a secrets manager.
☐ Medium Stateful resources such as databases set DeletionPolicy to Retain or Snapshot. Protect persistent data from unintended deletion with the appropriate policy.
☐ Medium Stack policies protect critical resources from unintended updates. Apply stack-level protection to sensitive resources.
☐ Medium cfn-lint and cfn-guard run in CI/CD against every template change. Validate templates and policy before deployment.
☐ Low Stack drift detection runs on a schedule for production stacks. Compare deployed resources with their expected CloudFormation configuration.

Azure and Google Cloud equivalents

The same control families exist on other clouds. This table maps the most common AWS items:

Control AWS Azure Google Cloud
Broad permissions Wildcard Action and Resource in IAM policies Owner or Contributor assignments at subscription scope, custom roles with * actions Primitive roles (Owner, Editor) and broad admin role bindings
Open administrative ports Security group ingress from 0.0.0.0/0 Network security group rules allowing any source Firewall rules allowing 0.0.0.0/0
Public storage S3 public access block and bucket policies Storage account blob public access allUsers and allAuthenticatedUsers bindings, public access prevention
Customer-managed encryption KMS keys Key Vault customer-managed keys Cloud KMS customer-managed encryption keys
Audit logging CloudTrail Activity Log and diagnostic settings Cloud Audit Logs, including Data Access logs
Secrets storage Secrets Manager Key Vault Secret Manager

How Invicti covers IaC security

Invicti IaC Security scans infrastructure definitions before deployment and can also orchestrate and ingest findings from external IaC security scanners. It brings these results into the Invicti Platform with orchestration, normalization, deduplication, and cross-engine correlation, so overlapping detections can be managed as part of the same AppSec workflow. Scans can run as part of development and CI/CD workflows, with policies that can block builds or deployments when defined thresholds are exceeded.

The misconfiguration categories line up with this checklist: public storage, disabled encryption, missing audit logging, wildcard and over-provisioned IAM permissions, open security groups, insecure defaults, and secrets embedded in infrastructure definitions.

All IaC scanning has a structural limit. It checks definitions before deployment, but it isn’t continuous monitoring of cloud accounts, it doesn’t verify repository settings such as branch protection, and it can’t tell you whether a deployed application is exploitable. It also doesn’t detect application-layer vulnerabilities such as SSRF, SQL injection, cross-site scripting (XSS), broken authentication, or broken object level authorization.

IaC scanning is one part of a broader cloud application security strategy that spans infrastructure definitions, containers, API security, application code, and running applications. Connecting findings across those layers provides the context needed to understand how an infrastructure weakness relates to application risk.

Connecting IaC findings to application risk

Invicti’s application security posture management (ASPM) consolidates IaC findings with dynamic application security testing (DAST), static application security testing (SAST), software composition analysis (SCA), secrets, and container findings, then uses signals such as runtime evidence, exploitability, business context, and threat intelligence to improve prioritization where those signals are available. Invicti DAST tests running applications and can automatically confirm exploitability for many supported vulnerability types using proof-based scanning.

The layers matter together. Consider a DAST scan that confirms an SSRF vulnerability in an application. Whether that flaw can reach cloud credentials depends on the infrastructure around it: the permissions on the role the workload runs as, and whether the instance metadata service requires IMDSv2. Those are IaC checklist items. A team looking at the confirmed application flaw and an overpermissive role finding side by side can make a better call on what to fix first than either finding supports alone.

Use this checklist, automate it, and add runtime context

This checklist provides the policy baseline: the controls your IaC scanning tools should enforce, your code reviews should validate, and your CI/CD gates should evaluate.

Invicti IaC Security checks infrastructure definitions before deployment. Invicti ASPM brings those findings together with the rest of your application security data, while Invicti DAST adds runtime evidence about vulnerabilities in the applications that run on that infrastructure.

Used together, these layers help teams move from isolated infrastructure checks to a more complete view of application risk – before deployment and in running applications.

To see how Invicti can help automate IaC security checks, connect them with broader AppSec findings, and bring runtime context into prioritization, request a demo of the Invicti Platform.

Frequently asked questions

Frequently asked questions about IaC security

What are the most critical IaC security misconfigurations?

Critical IaC misconfigurations include wildcard IAM permissions, SSH or RDP open to the internet, public storage, publicly accessible databases, hardcoded credentials, privileged Kubernetes containers, and unencrypted Terraform state. Instances that allow IMDSv1 can also increase the impact of some application SSRF vulnerabilities by making instance-role credentials accessible through straightforward metadata requests.

How do you prevent hardcoded secrets in Terraform?

Use external secrets managers or service-managed credentials, such as RDS-managed master passwords. Keep resolved secret values out of state where the provider supports ephemeral values or write-only arguments, store state in an encrypted remote backend, exclude .tfvars and state files from Git, scan commits for secrets, and never set credential defaults in reusable modules. Marking a variable sensitive redacts it in CLI output but does not by itself remove it from state.

How do scanner rules map to an IaC security checklist?

Scanners map their checks to rule IDs. Checkov, for example, publishes a policy index of checks with CKV identifiers for IAM wildcards, encryption, open ports, privileged containers, and more. Invicti IaC Security includes native IaC scanning and can also ingest findings from external scanners, bringing those results into a common policy and vulnerability management workflow. Native check sets include CIS benchmarks for AWS, Azure, Google Cloud, and Kubernetes.

For your own policy documentation, map each checklist item to the rule ID or benchmark recommendation your scanner uses, then use those IDs to configure hard failures, warnings, and exceptions in CI/CD.

How does IaC scanning support compliance?

Many checklist items support compliance requirements around access control, encryption, logging, vulnerability management, monitoring, and change control. IaC scanning can provide evidence that defined infrastructure controls are checked before deployment.

It does not establish compliance by itself. Account configuration, deployed infrastructure, runtime behavior, operational processes, and other controls may require separate validation depending on the framework and environment.

Table of Contents