AWS security baseline check

Account Acme Retail (synthetic demo) (123456789012) · regions ap-south-1 · 02 Nov 2026, 11:39 UTC
Baseline score
31/100F
weighted by severity
Controls failing
15 of 25
3 critical · 5 high · 6 medium · 1 low
Controls passing
6
1 not applicable · 0 to review
Coverage
83%
of the baseline could be evaluated

Summary

The account was checked against 25 baseline controls covering identity, logging, data, network and secrets, using read-only access only. It scores 31/100 (grade F). The most urgent finding: S3 Bucket acme-demo-uploads has public access due to bucket policy (DA-02, critical). Public buckets are found by automated scanners within hours. Nothing was changed in the account. Every fix below is code or a command for you to review and apply; a retest scan confirms it.

Top risks

SeverityControlWhat we foundFirst step
critical DA-02 No bucket is public S3 Bucket acme-demo-uploads has public access due to bucket policy. (acme-demo-uploads)
aws s3api put-public-access-block --bucket <bucket> --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
critical NC-01 No SSH, RDP or database port is open to the whole internet Security group api (sg-1c5d8088dba90bc1a) has SSH port 22 open to the Internet. (sg-1c5d8088dba90bc1a)
aws ec2 revoke-security-group-ingress --group-id <sg> --protocol tcp --port 22 --cidr 0.0.0.0/0
critical SP-01 No secrets in Lambda variables, EC2 user data or launch templates Potential secret found in Lambda function acme-invoice-mailer variables -> SMTP Credentials in variable SMTP_PASSWORD, Generic Password in variable SMTP_PASSWORD. (acme-invoice-mailer)
Move each value to Secrets Manager or SSM Parameter Store (SecureString), read it at runtime, then rotate it - it must be treated as exposed.
high ID-02 Every console user has MFA User priya.sharma has Console Password enabled but MFA disabled. (priya.sharma)
aws iam list-virtual-mfa-devices # find users without a device, then enable MFA for each, or move people to IAM Identity Center
high ID-05 Full administrator rights are limited to a break-glass role AWS policy AdministratorAccess is attached and allows '*:*' administrative privileges. (AdministratorAccess)
aws iam list-entities-for-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess

All controls

Identity and access

Not evaluatedID-01 · Root user is protected (MFA on, no access keys)critical

The root user can do everything, including closing the account. A stolen root password or key is a total loss.

not evaluated (the scanner returned no result - see Method)
Prowler: iam_root_mfa_enabled, iam_no_root_access_key
FailID-02 · Every console user has MFAhigh

Passwords get phished and reused. MFA stops most account takeovers.

1 failing item, 1 passing
Evidence (1)
ResourceRegionFinding
arn:aws:iam::123456789012:user/priya.sharmaap-south-1User priya.sharma has Console Password enabled but MFA disabled.
How to fix
aws iam list-virtual-mfa-devices  # find users without a device, then enable MFA for each, or move people to IAM Identity Center
Console: IAM > Users > (user) > Security credentials > Assign MFA device.
Prowler: iam_user_mfa_enabled_console_access · CIS-7.0 2.10
PassID-03 · Access keys are rotated and unused keys removedhigh

Long-lived keys leak through laptops, CI logs and git history. Datadog found active keys over a year old at 59% of IAM users.

6 items checked, all passing
Prowler: iam_rotate_access_key_90_days, iam_user_accesskey_unused, iam_user_console_access_unused · CIS-7.0 2.11, CIS-7.0 2.12
FailID-04 · People get permissions through groups, roles or SSO - not attached to usersmedium

Permissions attached user by user drift and are never reviewed. Groups, roles and IAM Identity Center keep access auditable.

1 failing item, 1 passing
Evidence (1)
ResourceRegionFinding
arn:aws:iam::123456789012:user/ci-deployap-south-1User ci-deploy has the policy AdministratorAccess attached.
How to fix
aws iam detach-user-policy --user-name <user> --policy-arn <arn>   # after adding the user to a group with the same rights
Console: Prefer IAM Identity Center: one login per person, permission sets per job, no long-lived keys.
Prowler: iam_policy_attached_only_to_group_or_roles · CIS-7.0 2.13
FailID-05 · Full administrator rights are limited to a break-glass rolehigh

Every identity with AdministratorAccess is a way to lose the whole account. Most teams need two or three, not ten.

1 failing item, 1 passing
Evidence (1)
ResourceRegionFinding
arn:aws:iam::aws:policy/AdministratorAccessap-south-1AWS policy AdministratorAccess is attached and allows '*:*' administrative privileges.
How to fix
aws iam list-entities-for-policy --policy-arn arn:aws:iam::aws:policy/AdministratorAccess
Console: Replace AdministratorAccess with job-based policies (PowerUserAccess, ReadOnlyAccess, or service-scoped). Keep one MFA-protected break-glass role.
Prowler: iam_aws_attached_policy_no_administrative_privileges, iam_customer_attached_policy_no_administrative_privileges, iam_inline_policy_no_administrative_privileges · CIS-7.0 2.14
PassID-06 · Password policy requires long, non-reused passwordslow

Only matters for IAM users with console access, but auditors and customer questionnaires ask for it.

2 items checked, all passing
Prowler: iam_password_policy_minimum_length_14, iam_password_policy_reuse_24 · CIS-7.0 2.8, CIS-7.0 2.9
Not evaluatedID-07 · IAM Access Analyzer watches for external accessmedium

It flags buckets, roles and keys shared with other accounts or the public - for free.

not evaluated (the scanner returned no result - see Method)
Prowler: accessanalyzer_enabled

Logging and detection

PassLD-01 · CloudTrail records every region, with tamper-evident, private logscritical

Without CloudTrail there is no record of who did what - no investigation, and no basis for a breach report.

3 items checked, all passing
Prowler: cloudtrail_multi_region_enabled, cloudtrail_log_file_validation_enabled, cloudtrail_logs_s3_bucket_is_not_publicly_accessible · CIS-2.0 3.3, CIS-7.0 4.1, CIS-7.0 4.2
FailLD-02 · GuardDuty threat detection is onhigh

GuardDuty spots stolen credentials, crypto-mining and unusual API calls. Costs a few dollars a month for small accounts.

1 failing item
Evidence (1)
ResourceRegionFinding
arn:aws:guardduty:ap-south-1:123456789012:detector/unknownap-south-1GuardDuty is not enabled.
How to fix
Terraform: module.security_baseline: guardduty = true
aws guardduty create-detector --enable --finding-publishing-frequency FIFTEEN_MINUTES
Prowler: guardduty_is_enabled
FailLD-03 · AWS Config records configuration changesmedium

Config keeps a history of how each resource was configured - the evidence auditors and investigators ask for.

1 failing item
Evidence (1)
ResourceRegionFinding
arn:aws:config:ap-south-1:123456789012:recorderap-south-1AWS Config recorder 123456789012 is disabled.
How to fix
Terraform: module.security_baseline: config_recorder = true
Console: AWS Config > Get started > record all resources > deliver to the CloudTrail logs bucket.
Prowler: config_recorder_all_regions_enabled · CIS-7.0 4.3
FailLD-04 · Log groups keep logs for at least a yearmedium

Logs kept for days or weeks are gone when an incident is found months later. One year is the minimum many customers - and India's DPDP Rules - expect for security logs. ('Never expire' passes here; the cost check LOGS-001 flags its price.)

1 failing item, 1 passing
Evidence (1)
ResourceRegionFinding
arn:aws:logs:ap-south-1:123456789012:log-group:/acme/api:*ap-south-1Log Group /acme/api has less than 365 days retention period (7 days).
How to fix
bash aws-foundation/fix-pack/scripts/set-log-retention.sh --days 365   # dry run first; add --apply to change
Prowler: cloudwatch_log_group_retention_policy_specific_days_enabled
PassLD-05 · Spend alerts would catch abuse within a daymedium

A stolen key used for crypto-mining shows up first on the bill. Budgets with email alerts are free.

1 item checked, all passing
Native check: spend_alerts

Data protection

Not evaluatedDA-01 · S3 Block Public Access is on for the whole accountcritical

One account-wide switch stops any bucket from being made public by mistake - the cause of many data leaks.

not evaluated (the scanner returned no result - see Method)
Prowler: s3_account_level_public_access_blocks
FailDA-02 · No bucket is publiccritical

Public buckets are found by automated scanners within hours.

1 failing item, 11 passing
Evidence (1)
ResourceRegionFinding
arn:aws:s3:::acme-demo-uploadsap-south-1S3 Bucket acme-demo-uploads has public access due to bucket policy.
How to fix
aws s3api put-public-access-block --bucket <bucket> --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
# if the bucket serves a website, put CloudFront with origin access control in front instead
Prowler: s3_bucket_public_access, s3_bucket_policy_public_write_access, s3_bucket_public_list_acl, s3_bucket_public_write_acl · CIS-7.0 2.21
FailDA-03 · Data is encrypted by default (S3 and EBS)medium

Default encryption is free and protects disks, snapshots and buckets that get copied or shared by mistake.

1 failing item, 3 passing
Evidence (1)
ResourceRegionFinding
arn:aws:ec2:ap-south-1:123456789012:volumeap-south-1EBS Default Encryption is not activated.
How to fix
Terraform: module.security_baseline: ebs_encryption_by_default = true
aws ec2 enable-ebs-encryption-by-default --region <region>
Prowler: s3_bucket_default_encryption, ec2_ebs_default_encryption · CIS-1.5 2.1.1, CIS-7.0 6.1.1
PassDA-04 · Databases are encrypted, private, backed up and protected from deletionhigh

An RDS instance reachable from the internet, without backups, is one mistake away from a data leak or permanent loss.

4 items checked, all passing
Prowler: rds_instance_storage_encrypted, rds_instance_no_public_access, rds_instance_backup_enabled, rds_instance_deletion_protection · CIS-7.0 3.2.1, CIS-7.0 3.2.3
PassDA-05 · No snapshot is publiccritical

A public EBS or RDS snapshot hands a full copy of the disk or database to anyone.

4 items checked, all passing
Prowler: ec2_ebs_public_snapshot, rds_snapshots_public_access

Network and compute

FailNC-01 · No SSH, RDP or database port is open to the whole internetcritical

Open admin and database ports are scanned constantly; one weak password or unpatched service is enough.

1 failing item, 4 passing
Evidence (1)
ResourceRegionFinding
arn:aws:ec2:ap-south-1:123456789012:security-group/sg-1c5d8088dba90bc1aap-south-1Security group api (sg-1c5d8088dba90bc1a) has SSH port 22 open to the Internet.
How to fix
aws ec2 revoke-security-group-ingress --group-id <sg> --protocol tcp --port 22 --cidr 0.0.0.0/0
# use SSM Session Manager (no open port) or a VPN/allow-list instead
Prowler: ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_22, ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_3389, ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_mysql_3306, ec2_securitygroup_allow_ingress_from_internet_to_tcp_port_postgres_5432, ec2_securitygroup_allow_ingress_from_internet_to_all_ports · CIS-7.0 6.3, CIS-7.0 6.4
No resourcesNC-02 · Default security groups allow no trafficlow

Resources launched without a chosen group land in the default one; it should block everything.

no matching resources in the scanned regions
Prowler: ec2_securitygroup_default_restrict_traffic
FailNC-03 · Instances require IMDSv2high

IMDSv1 lets an SSRF bug in a web app read the instance's cloud credentials (the Capital One breach). Datadog found IMDSv2 enforced on only 49% of instances.

1 failing item
Evidence (1)
ResourceRegionFinding
arn:aws:ec2:ap-south-1:123456789012:instance/i-2d70a6f89d6a8d1bdap-south-1EC2 Instance i-2d70a6f89d6a8d1bd has IMDSv2 disabled or not required.
How to fix
Terraform: module.security_baseline: ec2_imdsv2_default = true # new instances
bash aws-foundation/fix-pack/scripts/require-imdsv2.sh --regions <region>   # dry run lists instances; --apply after testing one
aws ec2 modify-instance-metadata-options --instance-id <id> --http-tokens required --http-put-response-hop-limit 2
Prowler: ec2_instance_imdsv2_enabled, ec2_instance_account_imdsv2_enabled · CIS-7.0 6.7
FailNC-04 · Only intended instances have public IP addressesmedium

Every public instance is attack surface (and costs $3.65 a month per IPv4 address). Most belong behind a load balancer.

1 failing item
Evidence (1)
ResourceRegionFinding
arn:aws:ec2:ap-south-1:123456789012:instance/i-2d70a6f89d6a8d1bdap-south-1EC2 Instance i-2d70a6f89d6a8d1bd has a Public IP: 54.214.72.161 (ec2-54-214-72-161.ap-south-1.compute.amazonaws.com).
How to fix
Console: Move workloads to private subnets behind a load balancer; reach them with SSM Session Manager. Keep a public IP only where it is intended, and note it in the report.
Prowler: ec2_instance_public_ip

Secrets and pipelines

FailSP-01 · No secrets in Lambda variables, EC2 user data or launch templatescritical

Anyone with read-only access - including auditors and many SaaS tools - can read these fields.

1 failing item, 1 passing
Evidence (1)
ResourceRegionFinding
arn:aws:lambda:ap-south-1:123456789012:function:acme-invoice-mailerap-south-1Potential secret found in Lambda function acme-invoice-mailer variables -> SMTP Credentials in variable SMTP_PASSWORD, Generic Password in variable SMTP_PASSWORD.
How to fix
Console: Move each value to Secrets Manager or SSM Parameter Store (SecureString), read it at runtime, then rotate it - it must be treated as exposed.
Prowler: awslambda_function_no_secrets_in_variables, ec2_instance_secrets_user_data, ec2_launch_template_no_secrets
FailSP-02 · Secrets Manager secrets rotate automaticallymedium

A secret that never changes stays valid after every leak.

1 failing item
Evidence (1)
ResourceRegionFinding
arn:aws:secretsmanager:ap-south-1:123456789012:secret:acme/prod/db-zTNKUhap-south-1SecretsManager secret acme/prod/db has rotation disabled.
How to fix
aws secretsmanager rotate-secret --secret-id <id> --rotation-lambda-arn <arn> --rotation-rules AutomaticallyAfterDays=90
Prowler: secretsmanager_automatic_rotation_enabled
FailSP-03 · CI/CD logs in with short-lived OIDC credentials, not stored keyshigh

Keys stored in CI were stolen in the tj-actions, Trivy and Checkmarx incidents. OIDC tokens expire in minutes.

1 failing item
Evidence (1)
ResourceRegionFinding
arn:aws:iam::123456789012:user/ci-deployglobalIAM user 'ci-deploy' looks like a CI/CD identity and has an active access key (created 2026-11-02); replace it with an OIDC role
How to fix
Terraform: gha-hardening-kit/terraform/github-oidc-role (service S2)
Console: Create an OIDC role trusted only by your repository's main branch or a protected environment, switch the workflow to role-to-assume, then delete the old access key.
Native check: ci_oidc
FailSP-04 · Container images are scanned on push and tags are immutablelow

Scanning catches known vulnerabilities before deployment; immutable tags stop a pushed image being silently replaced.

3 failing items
Evidence (3)
ResourceRegionFinding
arn:aws:ecr:ap-south-1:123456789012:registry/123456789012ap-south-1ECR registry 123456789012 has BASIC scanning without automated scanning enabled.
arn:aws:ecr:ap-south-1:123456789012:repository/acme/apiap-south-1ECR repository acme/api has scan on push disabled.
arn:aws:ecr:ap-south-1:123456789012:repository/acme/apiap-south-1Repository acme/api does not have immutability configured.
How to fix
Terraform: module.security_baseline: ecr_scan_on_push = true
aws ecr put-image-tag-mutability --repository-name <repo> --image-tag-mutability IMMUTABLE
Prowler: ecr_registry_scan_images_on_push_enabled, ecr_repositories_scan_images_on_push_enabled, ecr_repositories_tag_immutability

Method and limits