What You'll Build
In this exercise you will enable AWS Security Hub on a fresh AWS account, evaluate its initial CIS AWS Foundations Benchmark compliance score, identify the highest-severity failing controls, and remediate four critical gaps: deleting root access keys, enabling account-level S3 public access block, enforcing the IAM password policy, and revoking unrestricted SSH security group rules. You will then enable GuardDuty and Inspector, re-run the compliance check, and document the improved score.
By the end, the account will have addressed the most critical CIS Level 1 failures and will have continuous threat detection and vulnerability scanning active, establishing the security monitoring baseline that all subsequent production workloads will run within. The improved compliance score and resolved finding count serve as evidence that the account meets the minimum security baseline required for production use.
Prerequisites
- An AWS account where you have administrator access—either an IAM user with AdministratorAccess or an AWS Organization member account.
- AWS CLI configured with credentials that have securityhub:*, guardduty:*, inspector2:*, iam:*, s3control:*, and ec2:* permissions.
- Python 3.x and the AWS CLI v2 installed locally for the audit and remediation commands.
- Awareness that enabling GuardDuty, Inspector, and Security Hub in a fresh account will incur small but non-zero costs—approximately $2-5 for the duration of this exercise.
- Access to the AWS Console as the root user for the root access key deletion step, since AWS CLI cannot delete root access keys.
Setup — Enable Security Hub and Baseline Audit
Enable Security Hub with both the CIS Benchmark and AWS Foundational Security Best Practices standards, then run the initial compliance query to generate the findings list. The initial enable may produce dozens of CRITICAL and HIGH findings for a fresh account—this is expected and reflects the gap between default account configuration and the CIS baseline.
# Prerequisites: AWS CLI configured with an IAM user or role that has
# SecurityHub:*, Config:*, GuardDuty:*, and ReadOnly permissions.
# 1. Enable Security Hub with CIS and AWS Foundational standards
aws securityhub enable-security-hub \
--enable-default-standards \
--region ap-south-1
# 2. Wait 2-3 minutes for initial findings to populate, then check standards
aws securityhub describe-standards-controls \
--standards-subscription-arn \
$(aws securityhub get-enabled-standards \
--query 'StandardsSubscriptions[?contains(StandardsArn,`cis-aws`)].StandardsSubscriptionArn' \
--output text) \
--query 'Controls[?ComplianceStatus==`FAILED`].{ID:ControlId,Title:Title,Severity:SeverityRating}' \
--output table
# 3. Check overall compliance summary
aws securityhub get-summary-counts \
--query 'ResourceCount'
# 4. List all CRITICAL and HIGH NEW findings
aws securityhub get-findings \
--filters '{
"SeverityLabel": [{"Value":"CRITICAL","Comparison":"EQUALS"},{"Value":"HIGH","Comparison":"EQUALS"}],
"WorkflowStatus": [{"Value":"NEW","Comparison":"EQUALS"}],
"RecordState": [{"Value":"ACTIVE","Comparison":"EQUALS"}]
}' \
--query 'Findings[*].{Title:Title,Severity:Severity.Label,Resource:Resources[0].Id}' \
--output tableStep 1 — Remediate Critical IAM and S3 Findings
Address the two most critical findings first: CIS 1.4 (root access keys must not exist) and CIS 2.1.1 (account-level S3 public access block). The root access key deletion requires console access as the root user—this is the one security operation that cannot be performed via CLI unless you are already authenticated as root, which is the very problem the control is trying to prevent. After deletion, Security Hub re-evaluates the control automatically within minutes.
# Step 1: Fix CIS 1.4 — delete root access keys.
# Check if root access keys exist
aws iam get-account-summary \
--query 'SummaryMap.AccountAccessKeysPresent'
# If output is 1, root access keys exist — must delete.
# To delete root access keys, log in to the AWS Console as root:
# IAM → Security credentials → Access keys → Delete
# (Cannot be done via CLI unless using root credentials — which is the problem)
# After deletion, verify the finding is resolved:
aws securityhub get-findings \
--filters '{
"GeneratorId":[{"Value":"arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0/rule/1.4",
"Comparison":"PREFIX"}]
}' \
--query 'Findings[0].{Status:WorkflowStatus,Title:Title}'
# Expected: after deletion, the finding transitions to RESOLVED within ~5 minutes.
# Step 1b: Fix CIS 2.1.1 — enable account-level S3 block public access.
aws s3control put-public-access-block \
--account-id $(aws sts get-caller-identity --query Account --output text) \
--public-access-block-configuration \
'BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true'
# Verify:
aws s3control get-public-access-block \
--account-id $(aws sts get-caller-identity --query Account --output text)
# Expected: all four fields true.Step 2 — Password Policy and Security Group Remediation
Configure the IAM account password policy to meet CIS 1.8 requirements: minimum 14 characters, all character classes required, 90-day maximum age, 24-password reuse prevention. Then identify all security groups with port 22 open to the public internet and remove the unrestricted rules. For any instance that requires administrative access, replace the open SSH rule with AWS Systems Manager Session Manager access—no inbound rules required.
# Step 2: Fix CIS 1.8 — enforce IAM password policy.
aws iam update-account-password-policy \
--minimum-password-length 14 \
--require-uppercase-characters \
--require-lowercase-characters \
--require-numbers \
--require-symbols \
--max-password-age 90 \
--password-reuse-prevention 24 \
--allow-users-to-change-password
# Verify:
aws iam get-account-password-policy
# Expected: all 6 requirements true, maxPasswordAge 90, reusePreventionCount 24.
# Step 2b: Fix CIS 4.1 — identify and restrict security groups with public SSH.
# Find all security groups with port 22 open to 0.0.0.0/0 or ::/0
aws ec2 describe-security-groups \
--filters \
'Name=ip-permission.from-port,Values=22' \
'Name=ip-permission.to-port,Values=22' \
'Name=ip-permission.cidr,Values=0.0.0.0/0' \
--query 'SecurityGroups[*].{ID:GroupId,Name:GroupName,VPC:VpcId}' \
--output table
# For each offending security group, remove the unrestricted rule
# and replace with a specific IP range or remove SSH entirely:
aws ec2 revoke-security-group-ingress \
--group-id sg-XXXXXXXXXX \
--protocol tcp \
--port 22 \
--cidr 0.0.0.0/0
# Best practice: replace with AWS Systems Manager Session Manager
# (no SSH required — all access through IAM-authenticated SSM sessions).Step 3 — Enable Continuous Threat Detection
Enable GuardDuty with S3 logs, Kubernetes audit log, and malware protection data sources, and enable Inspector for EC2 and ECR resource types. These services begin generating findings immediately: Inspector will scan any ECR images already in the account's registry, and GuardDuty will begin analysing CloudTrail events for threat patterns. Check for any existing Inspector CRITICAL findings against ECR images as a bonus compliance check.
# Step 3: Enable and verify GuardDuty, Inspector, and CloudTrail.
# Enable GuardDuty if not already enabled
aws guardduty create-detector \
--enable \
--data-sources '{
"S3Logs":{"Enable":true},
"Kubernetes":{"AuditLogs":{"Enable":true}},
"MalwareProtection":{"ScanEc2InstanceWithFindings":{"EbsVolumes":{"Enable":true}}}
}' \
--region ap-south-1
# Verify GuardDuty is enabled:
aws guardduty list-detectors
# Expected: at least one detector ID returned.
# Enable Inspector v2 for EC2 and ECR
aws inspector2 enable \
--resource-types EC2 ECR
# Verify Inspector is enabled:
aws inspector2 describe-organization-configuration 2>/dev/null || \
aws inspector2 get-configuration
# Check for Inspector ECR findings (may take a few minutes after enablement):
aws inspector2 list-findings \
--filter-criteria '{
"resourceType":[{"comparison":"EQUALS","value":"AWS_ECR_CONTAINER_IMAGE"}],
"severity":[{"comparison":"EQUALS","value":"CRITICAL"}]
}' \
--query 'findings[*].{Title:title,Severity:severity,Image:resources[0].id}'Step 4 — Re-audit and Document Improvement
Wait 5-10 minutes for Security Hub to re-evaluate all controls after the remediations, then re-run the compliance count query to measure improvement. Mark the resolved findings as RESOLVED in the Security Hub workflow with a note documenting the remediation. The remaining failing controls form the backlog for the next remediation sprint, prioritised by severity.
# Step 4: Re-run the compliance check and record the improved score.
# Wait 5-10 minutes for Security Hub to re-evaluate all controls after fixes.
# Check the CIS compliance score after remediation:
aws securityhub describe-standards-controls \
--standards-subscription-arn \
$(aws securityhub get-enabled-standards \
--query 'StandardsSubscriptions[?contains(StandardsArn,`cis-aws`)].StandardsSubscriptionArn' \
--output text) \
--query '{
TotalControls: length(Controls),
PassingControls: length(Controls[?ComplianceStatus==`PASSED`]),
FailingControls: length(Controls[?ComplianceStatus==`FAILED`])
}'
# List remaining failures for the next remediation round:
aws securityhub describe-standards-controls \
--standards-subscription-arn \
$(aws securityhub get-enabled-standards \
--query 'StandardsSubscriptions[?contains(StandardsArn,`cis-aws`)].StandardsSubscriptionArn' \
--output text) \
--query 'Controls[?ComplianceStatus==`FAILED`].{ID:ControlId,Severity:SeverityRating,Title:Title}' \
--output table
# Mark the fixed findings as RESOLVED in Security Hub workflow:
# (Use the finding IDs from Step 1 to update their workflow status)
aws securityhub batch-update-findings \
--finding-identifiers '[{"Id":"<finding-id>","ProductArn":"<product-arn>"}]' \
--workflow '{"Status":"RESOLVED"}' \
--note '{"Text":"Remediated in Exercise 27","UpdatedBy":"india-squad-security"}'Warning: Enabling Security Hub generates findings immediately for all existing account resources. In a mature AWS account with many resources, this initial finding flood can reach thousands of findings. Do not attempt to suppress all findings to achieve a clean dashboard; this defeats the purpose of the exercise. Instead, focus on remediating the CRITICAL and HIGH findings first, using the --filters parameter on the get-findings API to work through findings by severity tier. Medium and Low findings can be addressed in subsequent sprints after the critical gaps are closed.
Extension Challenge: Create a Terraform module that encodes all the CIS Level 1 remediations from this exercise as infrastructure-as-code—password policy, S3 account block, Config rules for SSH/RDP, GuardDuty detector, Security Hub standards subscriptions—so that every new AWS account in the organisation can be bootstrapped to CIS Level 1 compliance in a single Terraform apply. Store the module in the GitOps repository and deploy it via ArgoCD's Terraform controller or Atlantis for GitOps-managed infrastructure.