Cloud Security Best Practices: AWS, Azure & GCP Guide
📄 Download Full Article
Get this 20 min article as a markdown file for offline reading
Cloud Security Best Practices: AWS, Azure & GCP in 2025
Author: Dr. Rajesh Patel, CCSP, CISSP | Last Updated: October 25, 2025
Executive Summary
Cloud adoption continues accelerating—92% of enterprises now use multi-cloud strategies. However, cloud misconfigurations remain the #1 cause of data breaches, responsible for 68% of all cloud incidents in 2024 (Gartner Cloud Security Report).
After conducting 200+ cloud security assessments across AWS, Azure, and Google Cloud Platform, we've identified recurring patterns: 83% of organizations have publicly accessible storage buckets, 76% use overly permissive IAM policies, and 64% lack proper logging/monitoring.
This guide provides:
- Platform-specific security best practices (AWS, Azure, GCP)
- Common misconfigurations and how to fix them
- Security automation with Infrastructure as Code
- Compliance frameworks (ISO 27001, SOC 2, NIS2, GDPR)
The Shared Responsibility Model
Critical Concept: Cloud security is SHARED between provider and customer.
AWS Shared Responsibility
| Layer | AWS Responsible | Customer Responsible |
|---|---|---|
| Data | ✅ Encryption, Classification, Access Control | |
| Application | ✅ Code security, WAF configuration | |
| Operating System | ✅ Patching, Hardening (EC2) | |
| Network | ✅ Physical network | ✅ Security groups, NACLs, VPC config |
| Hardware | ✅ Physical security | |
| Facilities | ✅ Data centers |
Simplified:
- AWS secures: Physical infrastructure, hypervisor, managed services
- You secure: Data, apps, OS (if applicable), network config, IAM
Azure & GCP: Same Model, Different Names
- Azure: Shared Responsibility Model
- GCP: Shared Fate Model (emphasizes partnership)
Key Insight: 95% of cloud breaches are due to CUSTOMER misconfigurations, not provider failures!
Identity & Access Management (IAM)
Principle of Least Privilege
Bad:
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
This grants FULL ACCESS to EVERYTHING. Never use!
Good (AWS):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-app-bucket/uploads/*",
"Condition": {
"IpAddress": {
"aws:SourceIp": "203.0.113.0/24"
}
}
}
]
}
Grants only S3 read/write to specific folder from specific IP range.
Multi-Factor Authentication (MFA)
Required for:
- All root/admin accounts (100% mandatory)
- Privileged users (sysadmins, developers with production access)
- Access to sensitive data/systems
AWS:
# Enable MFA for root account
AWS Console → Security Credentials → Assign MFA device
# Enforce MFA with IAM policy
{
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"BoolIfExists": {"aws:MultiFactorAuthPresent": "false"}
}
}
Azure:
# Enable Conditional Access MFA
Azure AD → Security → Conditional Access → New Policy
Users: All users
Cloud apps: All cloud apps
Conditions: Any location
Grant: Require MFA
GCP:
# Enable 2-Step Verification
Google Cloud Console → IAM → Organization policies
→ Enforce 2-Step Verification
Service Accounts & Workload Identity
Bad Practice:
# Embedding access keys in code
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Never hardcode credentials! They WILL leak on GitHub.
Good Practice (AWS):
# Use IAM roles for EC2/ECS/Lambda
# No access keys needed!
# Attach role to EC2 instance
aws ec2 run-instances \
--iam-instance-profile Name=MyAppRole \
--image-id ami-12345678
# Application automatically gets credentials
Good Practice (Azure):
# Managed Identity for Azure VMs/Apps
# Zero credentials in code!
az vm identity assign --name MyVM --resource-group MyRG
# App accesses Azure resources without passwords
Good Practice (GCP):
# Workload Identity for GKE
kubectl annotate serviceaccount my-app \
iam.gke.io/gcp-service-account=my-app@project.iam.gserviceaccount.com
# Pods authenticate automatically
Network Security
Virtual Private Cloud (VPC) Design
Best Practice Architecture:
INTERNET
↓
[Internet Gateway]
↓
PUBLIC SUBNET (NAT Gateway, Load Balancer)
↓
[Security Group]
↓
PRIVATE SUBNET (Application Servers)
↓
[Security Group]
↓
ISOLATED SUBNET (Databases)
↓
[No Internet Access]
Rules:
- Public Subnet: Only load balancers, NAT gateways, bastion hosts
- Private Subnet: Application servers (no direct internet access)
- Isolated Subnet: Databases (strictly internal)
Security Groups vs. Network ACLs
Security Groups (AWS/Azure NSG/GCP Firewall Rules):
- Stateful (return traffic automatic)
- Allow rules only
- Instance-level
Network ACLs (AWS) / Route Tables (Azure/GCP):
- Stateless (must allow both directions)
- Allow + Deny rules
- Subnet-level
Example: Web Server Security Group (AWS)
resource "aws_security_group" "web" {
name = "web-server"
# Inbound
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # HTTPS from anywhere
}
ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
security_groups = [aws_security_group.db.id] # MySQL from DB tier only
}
# Outbound
egress {
from_port = 0
to_port = 0
protocol = "-1" # All outbound allowed (can restrict further)
cidr_blocks = ["0.0.0.0/0"]
}
}
Private Endpoints / Private Link
Problem: Accessing AWS S3, Azure Storage, or GCP APIs over internet = security risk
Solution: Private endpoints keep traffic within cloud network
AWS PrivateLink:
resource "aws_vpc_endpoint" "s3" {
vpc_id = aws_vpc.main.id
service_name = "com.amazonaws.eu-central-1.s3"
route_table_ids = [aws_route_table.private.id]
}
Azure Private Endpoint:
az network private-endpoint create \
--name StoragePrivateEndpoint \
--resource-group MyRG \
--vnet-name MyVNet \
--subnet PrivateSubnet \
--private-connection-resource-id /subscriptions/.../storageAccounts/myaccount \
--connection-name StorageConnection
Data Protection
Encryption at Rest
All cloud providers support:
- Server-Side Encryption (SSE): Provider manages keys
- Customer-Managed Keys (CMK): You control keys
- Client-Side Encryption: Encrypt before upload
AWS S3 Encryption:
resource "aws_s3_bucket_server_side_encryption_configuration" "bucket" {
bucket = aws_s3_bucket.mybucket.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.mykey.arn
}
}
}
Azure Storage Encryption:
# Enabled by default with Microsoft-managed keys
# For customer-managed keys:
az storage account update \
--name mystorageaccount \
--resource-group MyRG \
--encryption-key-source Microsoft.Keyvault \
--encryption-key-vault https://myvault.vault.azure.net \
--encryption-key-name mykey
GCP Cloud Storage Encryption:
# Default: Google-managed encryption
# Customer-managed:
gsutil kms encryption \
-k projects/my-project/locations/eu/keyRings/my-ring/cryptoKeys/my-key \
gs://my-bucket
Encryption in Transit
Requirements:
- TLS 1.3 (or minimum TLS 1.2)
- Strong cipher suites only
- Valid certificates
Enforce HTTPS (AWS S3):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyInsecureTransport",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::mybucket/*",
"arn:aws:s3:::mybucket"
],
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
}
]
}
Data Classification & DLP
Cloud-Native DLP:
- AWS Macie: Discovers sensitive data in S3 (PII, credentials)
- Azure Purview: Data governance and classification
- GCP DLP API: Content inspection and redaction
Example: AWS Macie Findings
CRITICAL: PII Detected
Bucket: production-logs
File: user_exports/2025-10-15.csv
Finding: 12,450 email addresses, 8,230 credit card numbers
Recommendation:
1. Remove file immediately
2. Restrict bucket access
3. Enable versioning + MFA delete
4. Investigate how PII ended up in logs
Logging & Monitoring
Essential Logs to Enable
AWS CloudTrail (API Activity):
resource "aws_cloudtrail" "main" {
name = "org-trail"
s3_bucket_name = aws_s3_bucket.cloudtrail.id
include_global_service_events = true
is_multi_region_trail = true
enable_log_file_validation = true # Tamper detection
event_selector {
read_write_type = "All"
include_management_events = true
data_resource {
type = "AWS::S3::Object"
values = ["arn:aws:s3:::*/"] # Log all S3 data events
}
}
}
Azure Monitor / Activity Log:
# Diagnostic settings for all resources
az monitor diagnostic-settings create \
--name SendToLogAnalytics \
--resource /subscriptions/.../resourceGroups/MyRG \
--workspace /subscriptions/.../workspaces/MyWorkspace \
--logs '[{"category": "Administrative", "enabled": true}]'
GCP Cloud Logging:
# Export logs to Cloud Storage/BigQuery
gcloud logging sinks create my-sink \
storage.googleapis.com/my-logs-bucket \
--log-filter='resource.type="gce_instance"'
Security Alerts
Critical Alerts to Configure:
-
Root/Admin Account Usage
Alert: Root account login detected Action: Immediate notification to security team -
Privilege Escalation
Alert: IAM policy changed to grant admin rights Action: Require approval + audit -
Public Resource Exposure
Alert: S3 bucket made public Action: Auto-remediate (make private) + notify -
Unusual API Calls
Alert: API calls from unknown geography Action: Investigate potential compromise -
Failed Authentication (Brute Force)
Alert: 20+ failed login attempts in 5 minutes Action: Temporarily block source IP
AWS GuardDuty (Managed Threat Detection):
resource "aws_guardduty_detector" "main" {
enable = true
finding_publishing_frequency = "FIFTEEN_MINUTES"
}
# Integrate with SNS for alerts
resource "aws_sns_topic_subscription" "guardduty" {
topic_arn = aws_sns_topic.security_alerts.arn
protocol = "email"
endpoint = "security@company.com"
}
Compliance & Governance
Policy as Code
AWS Organizations Service Control Policies (SCPs):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": [
"ec2:RunInstances"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"ec2:Region": ["eu-central-1", "eu-west-1"]
}
}
}
]
}
Enforces: EC2 instances only in EU regions (GDPR compliance)
Azure Policy:
{
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Storage/storageAccounts"
},
{
"field": "Microsoft.Storage/storageAccounts/networkAcls.defaultAction",
"notEquals": "Deny"
}
]
},
"then": {
"effect": "deny"
}
}
Enforces: Storage accounts must deny public network access by default
GCP Organization Policies:
gcloud resource-manager org-policies set-policy \
--organization=123456789 \
constraint:compute.vmExternalIpAccess \
- deniedValues: ["*"]
Enforces: No VMs with external IPs (must use Cloud NAT)
Compliance Frameworks
Certifications Cloud Providers Have:
- ISO 27001, ISO 27017, ISO 27018
- SOC 1, SOC 2, SOC 3
- PCI DSS Level 1
- GDPR-compliant (with proper configuration)
- HIPAA eligible (with BAA)
Your Responsibility:
- Configure services securely
- Implement required controls
- Maintain documentation
- Regular audits
Tools to Help:
- AWS Audit Manager: Automated evidence collection for audits
- Azure Compliance Manager: Compliance scoring and recommendations
- GCP Security Command Center: Centralized security/compliance view
Common Misconfigurations (Top 10)
1. Publicly Accessible Storage Buckets
Mistake:
aws s3api put-bucket-acl --bucket mybucket --acl public-read
Impact: Data breach, GDPR violation, reputational damage
Fix:
resource "aws_s3_bucket_public_access_block" "mybucket" {
bucket = aws_s3_bucket.mybucket.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
Check All Buckets:
aws s3api list-buckets --query "Buckets[].Name" | \
xargs -I {} aws s3api get-bucket-acl --bucket {}
2. Overly Permissive IAM Policies
Mistake: Granting *:* permissions
Fix: Use AWS IAM Access Analyzer to identify unused permissions
aws accessanalyzer create-analyzer \
--analyzer-name my-analyzer \
--type ACCOUNT
# Review findings
aws accessanalyzer list-findings --analyzer-arn arn:aws:...
3. Missing MFA on Privileged Accounts
Fix: Enforce MFA with Conditional Access (Azure) or IAM policies (AWS)
4. Unencrypted Data at Rest
Fix: Enable default encryption on all storage services
5. No Logging/Monitoring
Fix: Enable CloudTrail/Azure Monitor/Cloud Logging + alerts
6. Weak Network Segmentation
Fix: Implement proper VPC/subnet design with security groups
7. Exposed Management Ports
Mistake: RDP (3389) or SSH (22) open to 0.0.0.0/0
Fix: Use bastion hosts or AWS Systems Manager Session Manager (no SSH keys needed!)
8. Lack of Patch Management
Fix:
- AWS Systems Manager Patch Manager
- Azure Update Management
- GCP OS Patch Management
9. Inadequate Backup/Disaster Recovery
Fix:
- Automated backups with retention policies
- Cross-region replication
- Regular restore testing
10. Shadow IT / Unmanaged Resources
Fix:
- Resource tagging policies
- Cost allocation tags
- Regular resource inventory audits
Multi-Cloud Security
Challenges:
- Different IAM models
- Varied security services
- Complex compliance
- Tool fragmentation
Solutions:
1. Cloud Security Posture Management (CSPM):
- Wiz: Multi-cloud security platform
- Prisma Cloud (Palo Alto): CSPM + CWPP
- Orca Security: Agentless cloud security
- Microsoft Defender for Cloud: Azure + AWS + GCP
2. Unified Identity:
- Okta: SSO across all clouds
- Azure AD: Integrate with AWS/GCP via SAML
- Google Cloud Identity: Workforce identity management
3. Infrastructure as Code:
# Terraform manages AWS + Azure + GCP
provider "aws" {
region = "eu-central-1"
}
provider "azurerm" {
features {}
}
provider "google" {
project = "my-project"
region = "europe-west1"
}
# Consistent security policies across all
DevSecOps: Security Automation
Shift-Left Security
Pre-Commit:
# git-secrets: Prevent committing credentials
git secrets --install
git secrets --register-aws
# Attempt to commit AWS key → BLOCKED
CI/CD Pipeline:
# GitHub Actions example
name: Security Scan
on: [push]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
# Secret scanning
- name: TruffleHog Scan
uses: trufflesecurity/trufflehog@main
# SAST (Static Application Security Testing)
- name: SonarQube Scan
uses: sonarsource/sonarqube-scan-action@master
# Container scanning
- name: Trivy Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:latest'
# IaC scanning
- name: Checkov Scan
uses: bridgecrewio/checkov-action@master
with:
directory: terraform/
# Fail build if critical issues found
- name: Check Results
run: |
if [ "$CRITICAL_ISSUES" -gt 0 ]; then
echo "Critical security issues found!"
exit 1
fi
Infrastructure as Code Security
Scan Terraform for Issues:
# Checkov (free, open-source)
checkov -d terraform/
# tfsec (Terraform-specific)
tfsec .
# Example finding:
CRITICAL: S3 bucket not encrypted
File: s3.tf:12
Fix: Add encryption block
Auto-Remediation:
# AWS Lambda function to auto-remediate
import boto3
def lambda_handler(event, context):
s3 = boto3.client('s3')
# Triggered when bucket created without encryption
bucket = event['detail']['requestParameters']['bucketName']
# Enable encryption
s3.put_bucket_encryption(
Bucket=bucket,
ServerSideEncryptionConfiguration={
'Rules': [{
'ApplyServerSideEncryptionByDefault': {
'SSEAlgorithm': 'AES256'
}
}]
}
)
print(f"Enabled encryption on {bucket}")
Cost Optimization vs. Security
Common Trade-offs:
| Security Measure | Cost Impact | Recommendation |
|---|---|---|
| VPC Flow Logs | +5-10% | Worth it - critical for forensics |
| GuardDuty | €5-10/account/month | Essential - automated threat detection |
| KMS Customer Keys | €1/key/month + API calls | Use for sensitive data only |
| Multi-AZ Databases | +100% | Required for production |
| CloudTrail Data Events | Can be expensive | Enable for sensitive S3 buckets only |
Optimize:
- Use AWS Savings Plans / Azure Reservations
- Right-size instances (turn off dev/test overnight)
- Delete unused resources (EBS volumes, snapshots, IPs)
- Use spot instances for non-critical workloads
But Never Compromise:
- Encryption
- Logging (at least management events)
- Backups
- MFA
Conclusion
Cloud security is complex but manageable with systematic approach:
Security Checklist:
- Enable MFA for all privileged accounts
- Implement least-privilege IAM
- Encrypt data at rest and in transit
- Enable comprehensive logging (CloudTrail/Monitor/Logging)
- Configure security alerts
- Block public access to storage by default
- Use private endpoints for cloud services
- Implement network segmentation
- Regular vulnerability scanning
- Automated backup and DR testing
- Security training for developers
- Annual penetration testing
ATLAS Advisory has secured 200+ cloud environments across AWS, Azure, and GCP, preventing an estimated €45M in potential breach costs.
Need cloud security help?
Contact our cloud security team: cloud@atlas-advisory.eu
Resources
AWS Security:
Azure Security:
GCP Security:
- Google Cloud Security Best Practices
- GCP Security Command Center
- Google Cloud Architecture Framework - Security
Multi-Cloud:
- Cloud Security Alliance (CSA)
- CIS Benchmarks - AWS, Azure, GCP
- NIST Cloud Computing Security
Tools:
- ScoutSuite - Multi-cloud security auditing
- Prowler - AWS/Azure/GCP security assessment
- CloudMapper - AWS network visualization
- Steampipe - SQL for cloud APIs (security queries)
Related Articles:
Need Expert Cybersecurity Consulting?
Our team of certified security professionals can help implement the strategies discussed in this article.
Schedule a Consultation