Five-Minute AWS Security Wins
10 AWS Security Hub findings you can close with single CLI commands — zero downtime, no maintenance windows, no application changes, and a decision framework for when to use CLI vs CDK.
- DATE:
- FEB.21.2026
- READ:
- 16 MIN
When you first open AWS Security Hub on an account that has never been hardened, the finding count is overwhelming. The natural instinct is to prioritize by severity — tackle critical findings first, work down to high, medium, low. But there’s a better strategy for day one: knock out every finding that can be fixed in under 5 minutes with zero downtime risk.
Quick wins dramatically reduce your finding count, improve your security posture score, and build momentum for the harder remediations that follow. This post covers 10 AWS Security Hub findings we fixed with single CLI commands. Together, they cleared over 40 findings in under 20 minutes with no service disruption and no application code changes.
Summary
+------------------+--------------------+----------+-------+ | Finding | Fix | Risk | Time | +------------------+--------------------+----------+-------+ | EC2.7 | EBS default | None | 1 min | | | encryption | | | +------------------+--------------------+----------+-------+ | ECR.2 | ECR tag | Low | 2 min | | | immutability | | | +------------------+--------------------+----------+-------+ | S3.13 | S3 lifecycle | None | 3 min | | | policies | | | +------------------+--------------------+----------+-------+ | RDS.6 | RDS enhanced | None | 2 min | | | monitoring | | | +------------------+--------------------+----------+-------+ | RDS.9 | RDS log publishing | None | 2 min | +------------------+--------------------+----------+-------+ | ELB.17 | ALB TLS 1.3 policy | Very Low | 1 min | +------------------+--------------------+----------+-------+ | S3.9 | S3 access logging | None | 2 min | +------------------+--------------------+----------+-------+ | S3.21 | S3 versioning | None | 1 min | +------------------+--------------------+----------+-------+ | CloudFormation.3 | Termination | None | 1 min | | | protection | | | +------------------+--------------------+----------+-------+ | ElastiCache.1 | Automatic backups | Low | 2 min | +------------------+--------------------+----------+-------+
The CLI-vs-CDK decision framework
Before fixing anything, decide whether the fix should be a CLI one-liner or go into CDK code:
Account-level settings (EBS default encryption, IMDSv2 defaults): always CLI. These are not resource-specific and can’t be expressed in CDK constructs.
CDK-managed resources with safe property changes (adding image_tag_mutability to an ECR construct): prefer CDK. But if you need the fix today, CLI first, CDK later.
CDK-managed resources where the property change causes resource replacement: CLI fix now to stop the bleeding, CDK fix in a planned maintenance window.
Non-CDK resources (manually created resources): CLI fix with documentation so it doesn’t drift back.
1. EBS default encryption (EC2.7)
A single command per region ensures all new EBS volumes are encrypted at rest:
aws ec2 enable-ebs-encryption-by-default --region us-east-1
# Verify:
aws ec2 get-ebs-encryption-by-default --region us-east-1
# {"EbsEncryptionByDefault": true}Existing volumes are unaffected. Only new volumes created after this change use default encryption. Zero cost — encryption is handled by the Nitro system with no performance impact.
If you have multiple regions:
for region in us-east-1 us-west-2 eu-west-1; do
aws ec2 enable-ebs-encryption-by-default --region "$region"
doneFor compliance frameworks requiring CMKs instead of AWS-managed keys:
KEY_ID=$(aws kms create-key
--description "Default EBS encryption key"
--query 'KeyMetadata.KeyId' --output text)
aws ec2 modify-ebs-default-kms-key-id --kms-key-id "$KEY_ID"2. ECR tag immutability (ECR.2)
Without immutability, anyone with push access can overwrite an existing image tag. IMMUTABLE_WITH_EXCLUSION protects release tags while still allowing deployment tags like latest and environment-specific tags to be overwritten:
# Define which tags stay mutable (CI/CD tags, environment tags)
EXCLUSION_FILTERS='[
{"filterType":"WILDCARD","filter":"qa*"},
{"filterType":"WILDCARD","filter":"dev*"},
{"filterType":"WILDCARD","filter":"staging*"},
{"filterType":"WILDCARD","filter":"latest"}
]'
# Apply to all repositories in a region
for repo in $(aws ecr describe-repositories
--query 'repositories[].repositoryName'
--output text --region us-east-1); do
aws ecr put-image-tag-mutability
--repository-name "$repo"
--image-tag-mutability IMMUTABLE_WITH_EXCLUSION
--image-tag-mutability-exclusion-filters "$EXCLUSION_FILTERS"
--region us-east-1
echo "Set: $repo"
doneCDK backport once the CLI fix is stable:
ecr.Repository(self, "AppRepo",
image_tag_mutability=ecr.TagMutability.IMMUTABLE,
...)3. S3 lifecycle policies (S3.13)
S3 buckets without lifecycle policies accumulate objects indefinitely. Add a policy that aborts incomplete multipart uploads (which are invisible in the console but accrue storage charges) and transitions old objects:
aws s3api put-bucket-lifecycle-configuration
--bucket my-app-files
--lifecycle-configuration '{
"Rules": [
{
"ID": "CleanupIncompleteMPU",
"Status": "Enabled",
"Filter": {"Prefix": ""},
"AbortIncompleteMultipartUpload": {"DaysAfterInitiation": 7}
},
{
"ID": "TransitionAndExpireOldVersions",
"Status": "Enabled",
"Filter": {"Prefix": ""},
"NoncurrentVersionExpiration": {"NoncurrentDays": 90}
}
]
}'4. RDS enhanced monitoring (RDS.6)
Enhanced monitoring provides OS-level metrics (CPU, memory, disk I/O) for RDS instances. Without it, CloudWatch only shows database-level metrics:
# Get or create the monitoring role
ROLE_ARN=$(aws iam get-role
--role-name rds-monitoring-role
--query 'Role.Arn' --output text 2>/dev/null ||
aws iam create-role
--role-name rds-monitoring-role
--assume-role-policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"monitoring.rds.amazonaws.com"},"Action":"sts:AssumeRole"}]}'
--query 'Role.Arn' --output text)
aws iam attach-role-policy
--role-name rds-monitoring-role
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonRDSEnhancedMonitoringRole
aws rds modify-db-instance
--db-instance-identifier my-postgres-db
--monitoring-interval 60
--monitoring-role-arn "$ROLE_ARN"
--apply-immediatelyNo downtime. The modification applies within a few minutes.
5. RDS log publishing (RDS.9)
Publish PostgreSQL logs to CloudWatch for long-term retention and alerting:
aws rds modify-db-instance
--db-instance-identifier my-postgres-db
--cloudwatch-logs-export-configuration
'{"EnableLogTypes": ["postgresql", "upgrade"]}'
--apply-immediatelyFor MySQL: use ["error", "general", "slowquery", "audit"] instead.
6. ALB TLS 1.3 policy (ELB.17)
The default ALB security policy (ELBSecurityPolicy-2016-08) allows TLS 1.0 and 1.1. Upgrade to TLS 1.3:
# Get all HTTPS listeners
LISTENERS=$(aws elbv2 describe-listeners
--query 'Listeners[?Protocol==`HTTPS`].ListenerArn'
--output text --region us-east-1)
for arn in $LISTENERS; do
aws elbv2 modify-listener
--listener-arn "$arn"
--ssl-policy ELBSecurityPolicy-TLS13-1-3-2021-06
echo "Upgraded: $arn"
done7. S3 access logging (S3.9)
Access logging records every request made to a bucket — useful for security audits and compliance:
# Create a dedicated logging bucket first
aws s3api create-bucket
--bucket my-app-access-logs
--create-bucket-configuration LocationConstraint=us-east-1
# Grant the logging service write access
aws s3api put-bucket-acl
--bucket my-app-access-logs
--acl log-delivery-write
# Enable logging on the source bucket
aws s3api put-bucket-logging
--bucket my-app-files
--bucket-logging-status '{
"LoggingEnabled": {
"TargetBucket": "my-app-access-logs",
"TargetPrefix": "my-app-files/"
}
}'8. S3 versioning (S3.21)
Versioning protects against accidental deletion and overwrites. Once enabled, it cannot be disabled (only suspended):
aws s3api put-bucket-versioning
--bucket my-app-files
--versioning-configuration Status=Enabled9. CloudFormation termination protection (CloudFormation.3)
Termination protection prevents accidental stack deletion with a single API call:
# Enable on a single stack
aws cloudformation update-termination-protection
--stack-name MyProductionStack
--enable-termination-protection
# Enable on all stacks in the account
for stack in $(aws cloudformation list-stacks
--stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE
--query 'StackSummaries[].StackName'
--output text); do
aws cloudformation update-termination-protection
--stack-name "$stack"
--enable-termination-protection
echo "Protected: $stack"
doneIn CDK, add it to app.py to protect all future stacks:
for child in app.node.children:
if isinstance(child, cdk.Stack):
child.termination_protection = True10. ElastiCache automatic backups (ElastiCache.1)
ElastiCache Redis without automatic backups risks data loss during cluster failures:
aws elasticache modify-replication-group
--replication-group-id my-redis-cluster
--snapshot-retention-limit 7
--apply-immediatelyFor single-node clusters:
aws elasticache modify-cache-cluster
--cache-cluster-id my-redis-node
--snapshot-retention-limit 7
--apply-immediatelyThis may trigger a brief maintenance window. Apply during low-traffic periods.
Putting it together: the consolidated script
#!/bin/bash
set -euo pipefail
REGION="us-east-1"
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
DB_IDENTIFIER="my-postgres-db"
REDIS_CLUSTER_ID="my-redis-cluster"
echo "=== EBS Default Encryption ==="
aws ec2 enable-ebs-encryption-by-default --region "$REGION"
echo "=== ECR Tag Immutability ==="
for repo in $(aws ecr describe-repositories
--query 'repositories[].repositoryName'
--output text --region "$REGION"); do
aws ecr put-image-tag-mutability
--repository-name "$repo"
--image-tag-mutability IMMUTABLE_WITH_EXCLUSION
--image-tag-mutability-exclusion-filters
'[{"filterType":"WILDCARD","filter":"latest"},{"filterType":"WILDCARD","filter":"dev*"}]'
--region "$REGION"
done
echo "=== ALB TLS Policy Upgrade ==="
for arn in $(aws elbv2 describe-listeners
--query 'Listeners[?Protocol==`HTTPS`].ListenerArn'
--output text --region "$REGION"); do
aws elbv2 modify-listener
--listener-arn "$arn"
--ssl-policy ELBSecurityPolicy-TLS13-1-3-2021-06
done
echo "=== RDS Enhanced Monitoring ==="
aws rds modify-db-instance
--db-instance-identifier "$DB_IDENTIFIER"
--monitoring-interval 60
--apply-immediately
echo "=== ElastiCache Backups ==="
aws elasticache modify-replication-group
--replication-group-id "$REDIS_CLUSTER_ID"
--snapshot-retention-limit 7
--apply-immediately
echo "=== CloudFormation Termination Protection ==="
for stack in $(aws cloudformation list-stacks
--stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE
--query 'StackSummaries[].StackName' --output text); do
aws cloudformation update-termination-protection
--stack-name "$stack"
--enable-termination-protection
done
echo "Done. Re-scan Security Hub in 30 minutes to see resolved findings."Security posture progression
When we ran this remediation on our account, starting from 300+ findings:
- After quick wins (day 1): ~260 findings remaining
- After CDK changes (week 2): ~120 findings remaining
- After maintenance window changes (week 3): ~70 findings remaining
- After accepted risk documentation (week 4): ~40 findings (all accepted)
The quick wins alone moved the Security Hub score from approximately 24% to 55%. Each CLI command resolved multiple findings because many findings are the same control failing across multiple resources.
The key insight: don’t wait for a systematic remediation plan before starting. The quick wins don’t require a plan — they require a terminal window and 20 minutes.