npx skills add ...
npx skills add snyk/studio-recipes --skill iac-security
Infrastructure as Code security scanning for Terraform, Kubernetes, CloudFormation, and Azure ARM. Detects misconfigurations, security risks, and compliance violations before deployment. Use when: - User asks to scan Terraform files or modules - User mentions "infrastructure security" or "IaC scan" - User is working with Kubernetes manifests - User asks about CloudFormation or ARM template security - Agent is generating or modifying infrastructure code
npx skills add snyk/studio-recipes --skill iac-security
Comprehensive security scanning for Infrastructure as Code to catch misconfigurations before they become production vulnerabilities.
Core Principle: Security issues are cheaper to fix in code than in production.
| Platform | File Types |
|---|---|
| Terraform | .tf, .tf.json, .tfvars |
| Terraform Plan | JSON plan output (terraform show -json) |
| Kubernetes | .yaml / .yml with apiVersion + kind |
| Helm | Chart templates (requires Chart.yaml) |
| AWS CloudFormation | .json / .yaml with AWSTemplateFormatVersion |
| Azure ARM | .json with $schema ARM URL |
| Serverless Framework | serverless.yml |
Goal: Identify all IaC files that need scanning.
Check for these indicators to confirm IaC type:
.tf files, terraform.tfstate, provider blocksapiVersion/kind, directories named k8s, manifestsAWSTemplateFormatVersion key, Resources section with AWS types$schema containing deploymentTemplateThen determine scan scope: single file, directory, or recursive.
Goal: Run appropriate IaC security scan.
Goal: Understand and categorize misconfigurations.
| Severity | Risk Level | Examples |
|---|---|---|
| Critical | Immediate risk | Public S3, open security groups |
| High | Significant risk | Missing encryption, excessive perms |
| Medium | Moderate risk | Missing logging, broad IAM |
| Low | Best practice | Missing tags, suboptimal config |
Group issues for easier remediation:
Goal: Provide secure configuration fixes. Apply the pattern below to each finding; representative examples follow.
Goal: Confirm fixes are effective.
For Terraform, regenerate and scan the plan:
Create .snyk to manage exceptions:
For organization-specific requirements:
.tar.gz--rules option| Error | Solutions |
|---|---|
| Could not read Terraform state | Run terraform init; check state backend; scan .tf files directly |
| Invalid HCL syntax | Run terraform validate; check syntax; ensure all variables are defined |
| Could not parse plan file | Regenerate with terraform show -json; check Terraform version compatibility; verify JSON validity |
.snyk policy for accepted risksRun snyk_iac_scan with:
- path: tfplan.json
- scan: "planned-values" # or "resource-changes"Run snyk_iac_scan with:
- path: <directory>
- rules: <path to custom rules bundle>## IaC Security Scan Results
### Overview
| Severity | Count | Status |
|----------|-------|--------|
| Critical | X | 🔴 Block |
| High | Y | 🟠 Fix Required |
| Medium | Z | 🟡 Recommended |
| Low | W | 🔵 Optional |
### Critical Issues
| Resource | Issue | Location |
|----------|-------|----------|
| aws_s3_bucket.data | Public access enabled | main.tf:45 |
| aws_security_group.web | Open to 0.0.0.0/0 on port 22 | network.tf:23 |
### High Issues
| Resource | Issue | Location |
|----------|-------|----------|
| aws_rds_instance.db | Encryption not enabled | database.tf:12 |# Insecure
resource "aws_s3_bucket" "data" {
bucket = "my-bucket"
}
# Secure
resource "aws_s3_bucket" "data" {
bucket = "my-bucket"
}
resource "aws_s3_bucket_public_access_block" "data" {
bucket = aws_s3_bucket.data.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}# Insecure - open to world
resource "aws_security_group" "web" {
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # BAD
}
}
# Secure - restricted to VPN/internal range
resource "aws_security_group" "web" {
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["10.0.0.0/8"]
}
}# Secure
resource "aws_db_instance" "main" {
engine = "postgres"
instance_class = "db.t3.micro"
storage_encrypted = true
kms_key_id = aws_kms_key.rds.arn
deletion_protection = true
}apiVersion: v1
kind: Pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
containers:
- name: app
image: myapp
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
resources:
limits:
cpu: "500m"
memory: "512Mi"
requests:
cpu: "200m"
memory: "256Mi"apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: app-network-policy
spec:
podSelector:
matchLabels:
app: myapp
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: allowed-namespaceResources:
DataBucket:
Type: AWS::S3::Bucket
Properties:
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: aws:kms
KMSMasterKeyID: !Ref DataBucketKey
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: trueRun snyk_iac_scan with:
- path: <same directory>terraform plan -out=tfplan.new
terraform show -json tfplan.new > tfplan.new.json## Fix Verification
| Severity | Before | After | Change |
|----------|--------|-------|--------|
| Critical | 2 | 0 | -2 ✅ |
| High | 5 | 1 | -4 ✅ |
| Medium | 8 | 6 | -2 ✅ |
### Remaining Issues
- 1 High: Third-party module - opened issue
- 6 Medium: Accepted risk (documented)ignore:
SNYK-CC-TF-123:
- '*':
reason: 'Accepted risk - internal development environment'
expires: 2025-06-01
created: 2024-01-15