Cloud Security Posture Management: Hardening Your Cloud Environment
Systematically secure your cloud infrastructure. Covers CIS benchmarks, identity management, network segmentation, encryption, and compliance automation.
Cloud misconfigurations cause 49% of data breaches. Most aren’t sophisticated attacks — they’re open S3 buckets, overly permissive IAM roles, and unencrypted databases. The average organization has 3,500+ cloud misconfigurations at any given time. Many go undetected for months.
Cloud Security Posture Management (CSPM) is the practice of continuously auditing your cloud environment against security benchmarks, detecting misconfigurations, and automating remediation. This guide covers the systematic hardening process across identity, network, encryption, logging, and compliance.
Why Misconfigurations Happen
| Root Cause | Example | Frequency |
|---|---|---|
| Speed over security | Developer opens port to test, forgets to close it | Very common |
| Default-permissive settings | S3 buckets used to be public by default | Common (older accounts) |
| Complexity | 300+ AWS services, each with unique IAM actions | Constant |
| Shared responsibility confusion | ”Isn’t AWS responsible for security?” | Common |
| Drift | Console change not reflected in IaC, or vice versa | Very common |
| Inadequate guardrails | No SCPs, no budget alerts, no approval workflows | Common |
The shared responsibility model means the cloud provider secures the infrastructure, but you secure everything you put in it — configurations, data, access, and network rules.
Step 1: Audit Identity and Access Management
IAM is the most critical CSPM domain. Overprivileged users and stale credentials are the #1 cloud attack vector.
1.1 Find Overprivileged Users
# AWS — find users with AdministratorAccess (should be near-zero)
aws iam list-attached-user-policies --user-name admin-user \
| jq '.AttachedPolicies[].PolicyArn' \
| grep -i admin
# Azure — list Owner/Contributor role assignments at subscription level
az role assignment list --scope "/subscriptions/{sub-id}" \
--query "[?roleDefinitionName=='Owner' || roleDefinitionName=='Contributor']" \
--output table
Target: Zero human users with permanent admin access. Use JIT activation (PIM in Azure, temporary credentials in AWS) for all admin operations.
1.2 Enforce MFA Everywhere
# AWS — find users without MFA enabled
aws iam generate-credential-report
aws iam get-credential-report --output json | \
jq -r '.Content' | base64 -d | \
csvtool col 1,4,8 - | grep ",false"
Target: 100% MFA coverage. No exceptions — including service accounts (which should not have passwords at all, only roles or managed identities).
1.3 Service Account Hygiene
Service accounts (API keys, access keys, app registrations) are often the weakest link because they have no MFA, rarely get rotated, and frequently accumulate permissions over time.
| Check | AWS | Azure | Target |
|---|---|---|---|
| Rotate keys > 90 days | aws iam list-access-keys | Portal → AD → App registrations | 100% keys < 90 days old |
| Remove unused keys | Filter by --status Active, check last used | Filter by last sign-in date | Zero unused keys |
| Use roles, not keys | IAM Roles for EC2/Lambda/ECS | Managed Identities for VMs/Functions | Eliminate keys where possible |
| Scope to minimum | Custom policies with specific resources | Custom RBAC with specific scopes | No wildcard (*) actions |
1.4 Access Key Audit Script
# AWS — comprehensive access key audit
aws iam generate-credential-report > /dev/null 2>&1
sleep 5
aws iam get-credential-report --output json | \
jq -r '.Content' | base64 -d | \
python3 -c "
import csv, sys, datetime
reader = csv.DictReader(sys.stdin)
for row in reader:
user = row['user']
if row['access_key_1_active'] == 'true':
last_used = row.get('access_key_1_last_used_date', 'N/A')
print(f' ACTIVE KEY: {user} | Last used: {last_used}')
if row['mfa_active'] == 'false' and row['password_enabled'] == 'true':
print(f' NO MFA: {user}')
"
Step 2: Network Segmentation
The principle is simple: nothing should be publicly accessible unless it explicitly needs to be. Everything else goes in private subnets, accessed through private endpoints or VPN.
# AWS — audit security groups for 0.0.0.0/0 access (danger!)
aws ec2 describe-security-groups \
--query "SecurityGroups[?IpPermissions[?contains(IpRanges[].CidrIp, '0.0.0.0/0')]]" \
--output json | jq '.[].GroupId'
# Azure — find NSGs allowing all inbound traffic
az network nsg list \
--query "[].{Name:name, Rules:securityRules[?access=='Allow' && sourceAddressPrefix=='*' && direction=='Inbound']}" \
--output json
Architecture Pattern
Internet → WAF/CDN → Load Balancer → App Tier → DB Tier
│ │
Public Subnet Private Subnet
(with NAT GW) (no direct internet)
│
Private Endpoints
(storage, key vault,
databases, queues)
Network Segmentation Checklist
| Layer | Control | Implementation |
|---|---|---|
| Perimeter | WAF with OWASP ruleset | AWS WAF, Azure Front Door, Cloudflare |
| Public subnet | Only load balancers and NAT gateways | VPC/VNet design |
| Private subnet | Application servers, databases, caches | No public IPs, no internet gateways |
| Service access | Private endpoints for all PaaS services | PrivateLink (AWS), Private Endpoint (Azure) |
| Egress | Explicit egress rules, NAT Gateway with logging | Deny all, allow specific |
| DNS | Private DNS zones for internal resolution | Route53 Private Zones, Azure Private DNS |
Step 3: Encryption Everywhere
Encryption should be the default, not an opt-in. Modern cloud services encrypt at rest by default, but you must verify and enforce.
| Layer | At Rest | In Transit | Key Management |
|---|---|---|---|
| Storage (S3/Blob) | AES-256 (SSE-S3 default) | HTTPS enforced (bucket policy) | Consider CMK for regulated data |
| Database (RDS/SQL) | TDE or CMK encryption | TLS 1.2+ required, deny plaintext | Rotate CMK annually |
| Secrets | Key Vault / Secrets Manager | API access over TLS | Automatic rotation |
| Compute (EBS/Disks) | Encrypted EBS/managed disks | Internal mTLS between services | Default encryption policy |
| Backups | Encrypted by default | Encrypted transfer | Same key hierarchy as source |
# AWS — find unencrypted EBS volumes (should return zero results)
aws ec2 describe-volumes \
--query "Volumes[?Encrypted==\`false\`].{ID:VolumeId, Size:Size, State:State}" \
--output table
# Azure — find storage accounts without encryption (rare but possible on old accounts)
az storage account list \
--query "[?encryption.services.blob.enabled==\`false\`].{Name:name}" \
--output table
# AWS — enforce S3 bucket encryption via bucket policy
aws s3api put-bucket-encryption \
--bucket my-data-bucket \
--server-side-encryption-configuration '{
"Rules": [{"ApplyServerSideEncryptionByDefault": {"SSEAlgorithm": "aws:kms"}}]
}'
Step 4: Enable Logging and Detection
You cannot detect breaches if you are not logging. Enable comprehensive logging across all accounts and regions.
# AWS — enable CloudTrail in all regions with log validation
aws cloudtrail create-trail \
--name audit-trail \
--s3-bucket-name audit-logs-bucket \
--is-multi-region-trail \
--enable-log-file-validation
aws cloudtrail start-logging --name audit-trail
# Enable GuardDuty (ML-based threat detection)
aws guardduty create-detector --enable
Detection Tools by Cloud
| Capability | AWS | Azure | GCP |
|---|---|---|---|
| API activity logging | CloudTrail | Activity Log | Cloud Audit Logs |
| Threat detection | GuardDuty | Defender for Cloud | Security Command Center |
| SIEM | Security Hub + Detective | Sentinel | Chronicle |
| Anomaly detection | GuardDuty findings | Defender alerts | SCC findings |
| Compliance dashboards | Security Hub | Defender regulatory compliance | SCC compliance |
Key Alerts to Configure
- Root account login (AWS) or Global Admin sign-in (Azure) — should never happen in normal operations
- Security group changes — especially rules adding 0.0.0.0/0 access
- IAM policy changes — new admin policies or role modifications
- Encryption disabled — any resource with encryption turned off
- Public access enabled — S3 buckets, storage accounts, databases made public
Step 5: Automate Compliance Scanning
Manual audits are too slow and too infrequent. Automate CIS benchmark scanning and run continuously.
# AWS Security Hub — enable CIS benchmarks
aws securityhub enable-security-hub \
--enable-default-standards
# Prowler — open-source CIS benchmark scanner (comprehensive)
pip install prowler
prowler aws --compliance cis_2.0_aws
# Checkov — IaC security scanner (prevent misconfigs before deployment)
pip install checkov
checkov -d ./terraform/ --framework terraform
CSPM Tool Comparison
| Tool | Type | Cloud Support | Cost |
|---|---|---|---|
| AWS Security Hub | Native | AWS | Pay-per-finding |
| Azure Defender | Native | Azure + multi-cloud | Per-resource pricing |
| Prowler | Open source | AWS, Azure, GCP | Free |
| Checkov | Open source (IaC) | All (via IaC) | Free |
| Wiz | SaaS | All major clouds | Enterprise pricing |
| Prisma Cloud | SaaS | All major clouds | Enterprise pricing |
CSPM Checklist
- MFA enforced on 100% of human users (no exceptions)
- Zero human users with permanent AdministratorAccess or Owner roles
- Service accounts use roles/managed identities (not access keys)
- No security groups allowing 0.0.0.0/0 on SSH (22) or RDP (3389)
- Database tiers in private subnets only (no public IPs)
- All storage encrypted at rest with appropriate key management
- TLS 1.2+ enforced on all endpoints (deny plaintext connections)
- CloudTrail / Activity Log enabled in all regions
- Threat detection active (GuardDuty / Defender for Cloud)
- CIS benchmark compliance scan passing > 90%
- IaC security scanning in CI/CD pipeline (Checkov, tfsec)
- Critical alerts routed to on-call team (PagerDuty, Opsgenie)
:::note[Source] This guide is derived from operational intelligence at Garnet Grid Consulting. For security audits, visit garnetgrid.com. :::