Cloud Security/ Terraform / IaC Security
🏗️ IaC Security Module

Terraform & IaC
Security — Shift Left Controls

Comprehensive reference for securing Infrastructure-as-Code pipelines. Covers top Terraform misconfigurations, tfsec and Checkov rule mapping, secure HCL patterns, and CI/CD security gate implementation for AWS, Azure, and GCP.

tfsec
Static Analysis
Checkov
Policy-as-Code
KICS
Multi-IaC Scan
Sentinel
HCP Policy
OPA
Rego Policies
🔴 Top Misconfigs
🔍 tfsec Rules
✅ Checkov Rules
🔒 Secure Patterns
⚡ CI/CD Gate
S3 Bucket — Public Access Enabled
AWS / aws_s3_bucket
Critical

Missing aws_s3_bucket_public_access_block resource or block_public_acls set to false allows public read/write access to S3 buckets.

// VULNERABLE
# Missing public access block resource "aws_s3_bucket" "data" { bucket = "my-data-bucket" # No public access block! }
// SECURE
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 }
Security Group — Open to Internet
AWS / aws_security_group
Critical

Ingress rule with cidr_blocks = ["0.0.0.0/0"] on SSH (22), RDP (3389), or all ports exposes instances to the internet.

// VULNERABLE
ingress { from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] # CRITICAL }
// SECURE
ingress { from_port = 22 to_port = 22 protocol = "tcp" security_groups = [aws_security_group.bastion.id] # Use Bastion SG, not 0.0.0.0/0 }
EBS / Storage Encryption Disabled
AWS / azure_managed_disk / GCP
High

Managed disks, RDS instances, and block storage without explicit encryption configuration rely on default settings which may not use CMK.

// SECURE — AWS EBS CMK
resource "aws_ebs_volume" "data" { availability_zone = "ap-south-1a" size = 100 encrypted = true kms_key_id = aws_kms_key.ebs.arn }
RDS / Database Publicly Accessible
AWS / aws_db_instance
Critical

Setting publicly_accessible = true on RDS exposes the database endpoint to the internet. Always use private subnets with security group access only from application tier.

// SECURE
resource "aws_db_instance" "prod" { publicly_accessible = false db_subnet_group_name = aws_db_subnet_group.private.name vpc_security_group_ids = [aws_security_group.app.id] storage_encrypted = true kms_key_id = aws_kms_key.rds.arn multi_az = true }
CloudTrail Logging Disabled
AWS / aws_cloudtrail
Critical

Missing CloudTrail resource or enable_logging = false disables API call logging — attackers' first action after gaining access.

// SECURE
resource "aws_cloudtrail" "main" { name = "prod-trail" s3_bucket_name = aws_s3_bucket.audit.id is_multi_region_trail = true include_global_service_events = true enable_log_file_validation = true enable_logging = true }
Azure Storage — HTTP Allowed
Azure / azurerm_storage_account
High

enable_https_traffic_only = false allows unencrypted HTTP access to Azure storage. Always enforce HTTPS for all storage accounts.

// SECURE
resource "azurerm_storage_account" "main" { enable_https_traffic_only = true allow_nested_items_to_be_public = false min_tls_version = "TLS1_2" blob_properties { versioning_enabled = true delete_retention_policy { days = 30 } } }
GCP Service Account — Editor Role
GCP / google_project_iam_binding
Critical

Assigning roles/editor or roles/owner to a service account violates least privilege. Stolen key = project-wide access.

// VULNERABLE
resource "google_project_iam_binding" "bad" { role = "roles/editor" # NEVER DO THIS members = ["serviceAccount:${sa.email}"] }
// SECURE — use predefined role
resource "google_project_iam_member" "app" { role = "roles/cloudsql.client" member = "serviceAccount:${sa.email}" }
Hardcoded Secrets in Terraform
All providers
Critical

Secrets in .tf files or terraform.tfvars get committed to git. State files also capture secrets in plain text — use remote state with encryption.

// VULNERABLE
password = "MyS3cur3P@ss!" # IN GIT = BREACH
// SECURE — reference from Secrets Manager
data "aws_secretsmanager_secret_version" "db" { secret_id = "prod/db/password" } password = data.aws_secretsmanager_secret_version.db.secret_string

tfsec (now part of Trivy) is the most popular Terraform-specific static analysis tool. Run it locally or in CI/CD to catch misconfigurations before apply.

# Install tfsec (via Trivy) brew install tfsec # macOS curl -s https://raw.githubusercontent.com/aquasecurity/tfsec/master/scripts/install_linux.sh | bash # Basic scan tfsec . # With JUnit XML for CI (Jenkins, GitLab) tfsec . --format junit --out results.xml # Only CRITICAL and HIGH tfsec . --minimum-severity HIGH # Scan specific module tfsec ./modules/networking # Via Trivy (recommended) trivy config . --severity HIGH,CRITICAL
Key tfsec Rule IDs
Rule IDDescriptionSeverityProvider
aws-s3-block-public-aclsS3 bucket should have public access block configuredCriticalAWS
aws-s3-enable-bucket-encryptionS3 bucket should have encryption at rest enabledHighAWS
aws-s3-enable-bucket-loggingS3 bucket should have logging enabledMediumAWS
aws-ec2-no-public-ingress-sgrSecurity group rule should not permit ingress from 0.0.0.0/0CriticalAWS
aws-ec2-require-vpc-flow-logs-for-all-vpcsVPC Flow Logs should be enabled for all VPCsHighAWS
aws-cloudtrail-enable-all-regionsCloudTrail should be enabled in all regionsHighAWS
aws-cloudtrail-enable-log-validationCloudTrail log file validation should be enabledHighAWS
aws-rds-no-public-db-accessRDS should not be publicly accessibleCriticalAWS
aws-kms-auto-rotate-keysKMS keys should be set to rotate automaticallyMediumAWS
azure-storage-enforce-httpsStorage account HTTPS-only traffic should be enforcedHighAzure
azure-storage-no-public-accessStorage container should not be publicly exposedCriticalAzure
azure-keyvault-ensure-secret-expiryKey Vault secrets should have expiry datesMediumAzure
azure-network-no-public-ingressNSG inbound rule should not allow ALL inboundCriticalAzure
google-storage-no-public-accessCloud Storage bucket should not be publicly accessibleCriticalGCP
google-compute-no-public-ingressFirewall rule should not allow ingress from 0.0.0.0/0CriticalGCP
google-iam-no-project-level-service-account-impersonationSA should not have project-level actAs permissionHighGCP

Checkov by Prisma Cloud scans Terraform, CloudFormation, Kubernetes, Dockerfile, and ARM templates. 1000+ built-in checks, extensible with Python custom policies.

# Install Checkov pip install checkov # Scan Terraform directory checkov -d . --framework terraform # Only check specific checks checkov -d . --check CKV_AWS_21,CKV_AWS_18,CKV_AWS_19 # Output as JUnit XML (for CI integration) checkov -d . -o junitxml > results.xml # Fail on HIGH+ severity only checkov -d . --soft-fail-on LOW,MEDIUM # Scan with Terraform plan (catches dynamic values) terraform plan -out tf.plan ; terraform show -json tf.plan > tf.json checkov -f tf.json --framework terraform_plan
Critical Checkov Check IDs
Check IDWhat It ChecksResource
CKV_AWS_18Ensure S3 bucket has access logging enabledaws_s3_bucket
CKV_AWS_19Ensure S3 bucket has server-side encryption enabledaws_s3_bucket
CKV_AWS_20Ensure S3 bucket ACL does not allow public readaws_s3_bucket
CKV_AWS_21Ensure S3 bucket has versioning enabledaws_s3_bucket
CKV_AWS_25Ensure no security groups allow ingress from 0.0.0.0/0 to port 3389aws_security_group
CKV_AWS_24Ensure no security groups allow ingress from 0.0.0.0/0 to port 22aws_security_group
CKV_AWS_8Ensure AWS instances are not publicly exposedaws_instance
CKV_AWS_17Ensure RDS database is not publicly accessibleaws_db_instance
CKV_AWS_16Ensure RDS database has encryption enabled at-restaws_db_instance
CKV_AWS_67Ensure CloudTrail multi-region is enabledaws_cloudtrail
CKV_AWS_36Ensure CloudTrail log file validation is enabledaws_cloudtrail
CKV_AZURE_3Ensure Azure storage account allows HTTPS traffic onlyazurerm_storage_account
CKV_AZURE_6Ensure Azure SQL server enables AuditingPolicyazurerm_sql_server
CKV_AZURE_35Ensure Azure Key Vault is recoverableazurerm_key_vault
CKV_GCP_28Ensure GCS bucket does not allow public accessgoogle_storage_bucket
CKV_GCP_62Ensure Cloud Audit Logging is configured for all servicesgoogle_project_iam_audit_config
CKV2_GCP_5Ensure GCP default service account not used at project levelgoogle_project_iam_binding
Secure Terraform Patterns

Remote State with Encryption

terraform { backend "s3" { bucket = "tf-state-prod" key = "prod/terraform.tfstate" region = "ap-south-1" encrypt = true kms_key_id = "alias/tf-state-key" dynamodb_table = "tf-state-lock" # Never store state locally! } }

Variables — No Secrets in .tfvars

# Use environment variables for secrets export TF_VAR_db_password=$(aws secretsmanager get-secret-value \ --secret-id prod/db/password --query SecretString --output text) # Or reference Secrets Manager in data source data "aws_secretsmanager_secret_version" "db" { secret_id = "prod/db/password" } # .tfvars should NEVER be committed to git # Add *.tfvars to .gitignore

Least Privilege IAM Role Pattern

data "aws_iam_policy_document" "app" { statement { effect = "Allow" actions = ["s3:GetObject","s3:PutObject"] resources = ["${aws_s3_bucket.app.arn}/*"] # NOT "s3:*" — explicit actions only } statement { effect = "Allow" actions = ["kms:GenerateDataKey","kms:Decrypt"] resources = [aws_kms_key.app.arn] } }

Pre-commit Hook — tfsec + Checkov

# .pre-commit-config.yaml repos: - repo: https://github.com/antonbabenko/pre-commit-terraform hooks: - id: terraform_tfsec args: [--minimum-severity=HIGH] - id: terraform_checkov args: [--soft-fail-on=LOW,MEDIUM] - id: terraform_validate - id: detect-secrets
Security Gate Pipeline
💻
Code Commit
Developer pushes Terraform
🔍
Static Scan
tfsec + Checkov
Security Gate 1
🔐
Secret Scan
detect-secrets / gitleaks
Security Gate 2
📋
tf plan
Terraform plan output
📜
Policy Gate
OPA / Sentinel
Security Gate 3
👤
Peer Review
PR approval required
🚀
tf apply
Deploy to cloud
GitHub Actions Security Gate
# .github/workflows/terraform-security.yml name: Terraform Security Gate on: [pull_request] jobs: security-scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run tfsec uses: aquasecurity/tfsec-action@v1 with: minimum_severity: HIGH format: sarif - name: Run Checkov uses: bridgecrewio/checkov-action@v12 with: directory: . framework: terraform soft_fail: false skip_check: CKV2_GITHUB_1 # example skip - name: Scan for secrets (gitleaks) uses: gitleaks/gitleaks-action@v2 - name: Terraform Plan run: | terraform init terraform plan -out=tfplan.binary terraform show -json tfplan.binary > tfplan.json - name: Checkov Terraform Plan (catches dynamic values) uses: bridgecrewio/checkov-action@v12 with: file: tfplan.json framework: terraform_plan
IaC Misconfiguration Scanner — Simulation Tool

🔍 IaC Security Findings Generator

Select your cloud provider and Terraform resources. Get simulated findings with severity ratings, tfsec/Checkov rule IDs, and fix recommendations.