Safeguard
Cloud Security

How Snyk IaC identifies unencrypted storage resources acr...

Snyk IaC flags unencrypted S3 buckets, Azure Storage, and GCP disks by parsing Terraform and CloudFormation for missing encryption attributes before deployment.

Karan Patel
Cloud Security Engineer
Updated 8 min read

A single missing attribute in a Terraform file — no server_side_encryption_configuration block on an aws_s3_bucket, no encrypted = true on an EBS volume — is often the only difference between a compliant storage resource and one that ships to production readable in plaintext. Because encryption-at-rest gaps rarely throw errors during terraform plan or terraform apply, they tend to surface only during an audit, a penetration test, or a breach postmortem. Snyk IaC (Infrastructure as Code) is one of the more widely deployed tools that tries to catch this earlier, by scanning Terraform, CloudFormation, Kubernetes manifests, Helm charts, and ARM/Bicep templates before they're ever deployed — with Snyk AWS checks in particular covering S3, EBS, RDS, SNS, and SQS encryption attributes out of the box. This post walks through the publicly documented mechanics of how Snyk IaC identifies unencrypted storage resources across AWS, Azure, and GCP — what it actually parses, which attributes it checks, and where static analysis of configuration files inherently stops short of the real cloud environment.

How does Snyk IaC know a storage resource is unencrypted before it's even deployed?

It never touches the deployed resource at all — it reads the source file. Snyk IaC works by parsing the declarative configuration (a .tf file, a CloudFormation YAML/JSON template, a Kubernetes manifest) into a normalized internal representation of every resource block and its attributes, independent of the original syntax. That normalization step is what lets Snyk apply the same underlying "is encryption configured" check to an aws_s3_bucket written in HCL and an AWS::S3::Bucket written in CloudFormation JSON, without maintaining two entirely separate rule sets. Once a resource is represented internally, Snyk's policy engine evaluates it against a library of rules — Snyk has documented having thousands of built-in cloud configuration checks spanning AWS, Azure, GCP, and Kubernetes — and any resource whose declared configuration omits, disables, or misconfigures an expected encryption attribute is reported as a finding, with a file path and line number pointing straight back to the offending block. Nothing is deployed or provisioned to run this check; it's a source-level analysis that can run in a pull request, a CI job, or a developer's editor.

What exact configuration attributes does Snyk AWS scanning trigger a finding on?

On AWS, the checks center on the presence and value of encryption-specific fields that each resource type exposes in its own provider schema. For S3 buckets, Snyk looks for a server_side_encryption_configuration block (or, in newer Terraform AWS provider versions, a separate aws_s3_bucket_server_side_encryption_configuration resource tied to the bucket) — its absence is flagged as an unencrypted bucket. For EBS volumes, the check is a boolean: encrypted = true on aws_ebs_volume, or the account-level "encryption by default" setting when evaluating account configuration rather than a single resource. For RDS instances, the equivalent attribute is storage_encrypted = true; for SNS topics and SQS queues, it's the presence of a kms_master_key_id. Because these are all attribute-level checks against a known schema, Snyk can flag a bucket as non-compliant even if it's one of two hundred resources in a module, as long as the attribute is missing or explicitly set to false in the parsed configuration.

How does the same logic translate to Azure and GCP, where "encrypted" means something different?

It doesn't translate literally, because Azure and GCP have different default postures than AWS, so Snyk's rules are adapted per provider rather than copy-pasted. Azure Storage Accounts are encrypted at rest by the platform by default, so the more common Snyk finding here is a missing enable_https_traffic_only = true setting (forcing encryption in transit) or the absence of a customer-managed key configuration (customer_managed_key block) when an organization's policy requires keys it controls rather than Microsoft-managed keys. Azure SQL databases are checked for Transparent Data Encryption (TDE) status via the transparent_data_encryption resource or setting. On GCP, the picture is different again: Cloud Storage buckets and Compute Engine persistent disks are encrypted at rest by Google by default using Google-managed keys, so a bucket isn't "unencrypted" in the AWS sense just because no encryption block is present. Snyk's GCP checks instead typically look for the absence of a customer-managed encryption key (CMEK) configuration — an encryption block referencing a Cloud KMS key on google_storage_bucket or google_compute_disk — when an organization's compliance baseline (common in regulated environments needing SOC 2 or PCI-DSS evidence) requires customer control over key material rather than relying on the provider's default.

What runs these checks — is it a fixed rule list or a configurable engine?

It's a policy engine that evaluates rules against the parsed resource graph, and Snyk has publicly documented extending this beyond its built-in rule set. Since introducing custom rules for Infrastructure as Code, Snyk has allowed teams to write additional checks using Open Policy Agent's Rego policy language, layered on top of the same parsed representation used for the out-of-the-box AWS, Azure, and GCP checks. In practice this means the "missing encryption attribute" pattern isn't hardcoded per rule — it's a generic evaluation of a resource's attributes against a condition, which is why Snyk can ship new checks (for a newly released AWS resource type, for instance) without redesigning the parsing layer. Findings carry a severity rating (typically low through critical) and are mapped to compliance frameworks such as CIS Benchmarks, PCI-DSS, and SOC 2 controls, which is how a single "S3 bucket without encryption" finding ends up cited in multiple compliance reports simultaneously rather than being a one-off security note.

Where does this check actually run in a developer's workflow?

It runs wherever the IaC file lives — CLI, CI pipeline, pull request, or editor — rather than as a separate, later-stage gate. The snyk iac test command scans a local directory or file and returns findings in the terminal or as JSON/SARIF for tooling integration. The same scanning logic is wired into CI systems (GitHub Actions, GitLab CI, Jenkins, CircleCI) so that a Terraform plan can be evaluated as part of a build, and into pull request checks that comment directly on the diff introducing the unencrypted resource, before a reviewer approves the merge. IDE plugins for VS Code and JetBrains products surface the same findings as a developer writes the resource block, which is the earliest point in the workflow this kind of check can realistically run. This "shift-left" placement is the core value proposition: an encryption gap caught in a PR review costs a comment and a one-line diff, while the same gap caught in a compliance audit six months post-deployment costs a remediation project.

Can Snyk catch a storage resource that was encrypted at deploy time but changed later?

Static IaC scanning alone cannot, because it only ever sees the source file, not the live resource — this is a documented limitation rather than a gap unique to Snyk. If a bucket's encryption configuration is modified outside of the IaC pipeline (through the AWS console, a CLI command, or a resource created manually and never represented in Terraform at all), the source file Snyk scans no longer reflects reality, and no finding is generated because there's nothing in the parsed configuration to flag. Snyk has extended into cloud configuration scanning of live environments — capability that came into the product following its 2022 acquisition of Fugue — which polls cloud provider APIs read-only to evaluate the actual deployed state of resources, independent of what the IaC source says. That is a materially different mechanism from the static parsing described above: it's checking the resource as the cloud provider reports it right now, not the resource as declared in a .tf file. The two approaches are complementary — static IaC scanning stops a misconfiguration from being deployed, while live cloud scanning catches drift and out-of-band changes after the fact — but they are not the same check running twice.

How Safeguard Helps

Understanding exactly where a scanner like Snyk IaC draws its line — source-file parsing, provider-specific attribute checks, PR-time enforcement, and the gap that live drift detection is a separate mechanism — matters because most organizations run more than one of these tools across more than one stage of the pipeline, and the findings need to land somewhere coherent. Safeguard focuses on the software supply chain layer that sits around this kind of scanning: aggregating findings from IaC, SCA, and container scanners into a single view, tracking which unencrypted-storage findings were caught pre-deploy versus discovered later through drift, and preserving the evidence trail — who introduced the misconfiguration, which commit fixed it, which policy gate should have blocked it — that SOC 2 and other compliance audits actually ask for. Rather than replacing a tool like Snyk IaC, Safeguard's role is to make sure the signal it produces is enforced consistently across every service and repository in an organization's footprint, and that a remediation is provable rather than just reported.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.