Skip to content
ExponaBLOG
AWS FOR DEVELOPERS · DAY 078 / 100

An AWS security baseline: a hands-on guide for engineers

Services to turn on in every account.

Expona Admin7 min read

After five-and-a-half years building production systems across e-commerce, CRM, trading, and SaaS platforms, I've learned that security isn't something you bolt on later—it's a foundation. Every AWS account I spin up today follows the same baseline checklist: four core services that cost almost nothing but catch everything. Let me walk you through why an AWS security baseline matters and which services should be running in every account you own.

The accounts that got into trouble weren't the ones that planned for security early. They were the ones that assumed they'd handle it later, then shipped a product before "later" arrived. By that time, the blast radius is already expanding. An AWS security baseline isn't a compliance exercise—it's practical hygiene. It tells you what happened, what's currently wrong, and what's drifting out of your control.

CloudTrail — Records API activity across your accounts

CloudTrail is your audit log. Every API call made in your AWS account—whether by humans, applications, or automated systems—gets recorded. I think of it as mandatory accounting for cloud infrastructure.

Here's a real scenario: You wake up to an email that S3 bucket versioning suddenly disabled itself. Did a developer do it? Did malware do it? Was it an accidental terraform apply? Without CloudTrail, you're guessing. With it, you see exactly which IAM principal called which API at which time, including the JSON payload they sent. That's the difference between thirty minutes of investigation and three days of forensics.

CloudTrail has two operating modes. Management events (the default) capture control plane operations: creating EC2 instances, modifying IAM policies, changing RDS snapshots. Data events capture read/write activity on S3 objects and DynamoDB tables—more verbose, more expensive, but essential if S3 is your data layer and you need to prove who accessed what.

Set it up once, point it to an S3 bucket in a separate "audit" account, and enable CloudTrail organization trail if you have multiple AWS accounts. Don't store logs in the same account where bad actors might be operating—they'll try to cover their tracks.

Here's the minimal CloudFormation to get started:

bash
AWSTemplateFormatVersion: '2010-09-09'
Description: 'CloudTrail setup for audit logging'
Resources:
  AuditBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub 'audit-logs-${AWS::AccountId}'
      VersioningConfiguration:
        Status: Enabled
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true

  AuditBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref AuditBucket
      PolicyText:
        Version: '2012-10-17'
        Statement:
          - Sid: AWSCloudTrailAclCheck
            Effect: Allow
            Principal:
              Service: cloudtrail.amazonaws.com
            Action: 's3:GetBucketAcl'
            Resource: !GetAtt AuditBucket.Arn
          - Sid: AWSCloudTrailWrite
            Effect: Allow
            Principal:
              Service: cloudtrail.amazonaws.com
            Action: 's3:PutObject'
            Resource: !Sub '${AuditBucket.Arn}/*'
            Condition:
              StringEquals:
                's3:x-amz-acl': bucket-owner-full-control

  CloudTrailRole:
    Type: AWS::CloudTrail::Trail
    DependsOn: AuditBucketPolicy
    Properties:
      TrailName: organization-trail
      S3BucketName: !Ref AuditBucket
      IsLogging: true
      IsMultiRegionTrail: true
      IncludeGlobalServiceEvents: true
      EventSelectors:
        - ReadWriteType: All
          IncludeManagementEvents: true

One critical detail: CloudTrail has a 15-minute delivery lag. Don't expect real-time notifications. For that, combine it with EventBridge to react faster to critical events like root key usage or IAM policy changes.

GuardDuty — Threat detection from logs and network activity

GuardDuty is a managed threat detection service. It ingests CloudTrail logs, VPC Flow Logs, and DNS logs, then runs machine learning models over them to spot anomalies. You're paying for the detection engine, not the log storage.

I've seen GuardDuty catch things that would have slipped past manual review: an EC2 instance communicating with a known command-and-control server, a Lambda function exfiltrating data to an external IP, brute-force SSH attempts on an exposed bastion host. These aren't exotic attacks—they're real things that happen when you're operating at scale.

GuardDuty findings come in three severity levels: Low, Medium, High. Early in a product lifecycle, I'd investigate all Medium and High findings. As you mature and tune your rules, you'll create suppressions for known false positives (like your security scanning tools hitting your own APIs).

Enable GuardDuty in the AWS Security Hub console or via CLI:

bash
aws guardduty create-detector \
  --enable \
  --finding-publishing-frequency FIFTEEN_MINUTES \
  --region us-east-1

Then configure SNS notifications so findings land in Slack or your incident response channel:

bash
aws guardduty create-publishing-destination \
  --detector-id YOUR_DETECTOR_ID \
  --destination-type SNS \
  --destination-properties DestinationArn=arn:aws:sns:us-east-1:ACCOUNT:guardduty-findings

GuardDuty isn't free—roughly $0.30 per million CloudTrail events per month, plus data ingestion costs—but for a production account, it's non-negotiable. The cost of missing a real breach far exceeds the detection bill.

Security Hub — Central findings and best-practice checks

Security Hub aggregates findings from GuardDuty, AWS Config, IAM Access Analyzer, and other services into a single dashboard. Think of it as your security control center.

Beyond aggregation, Security Hub runs continuous compliance checks against your infrastructure. Did someone create a public S3 bucket? Are your EBS volumes encrypted? Is your RDS database publicly accessible? Security Hub catches all of these and flags them with a severity score.

The compliance standards it supports include CIS AWS Foundations, PCI DSS, HIPAA, NIST, and SOC 2. If your startup is aiming at enterprises or regulated industries, Security Hub gives you a head start on compliance reviews.

Enable Security Hub:

bash
aws securityhub enable-security-hub \
  --tags Environment=production \
  --region us-east-1

Then enable the standards you care about:

bash
aws securityhub batch-enable-standards \
  --standards-subscription-requests StandardsArn=arn:aws:securityhub:us-east-1::standards/aws-foundational-security-best-practices/v/1.0.0 \
  --region us-east-1

Security Hub's dashboard shows you a rolling score of compliance posture. On Day 1, expect to see dozens of failing checks. That's normal. Your job is to triage them, fix the legitimate ones, and suppress false positives. I typically start by fixing anything marked "Critical," then work down to "Medium" as time allows.

AWS Config — Track resource changes and compliance rules

AWS Config answers a deceptively simple question: what does my infrastructure look like right now, and when did it change?

I used Config to solve a problem at a previous company: a developer accidentally modified the database security group to allow 0.0.0.0/0 inbound on port 5432. No alert fired. We discovered it weeks later during a security audit. With Config, we would have seen that change recorded and could have triggered an auto-remediation to revert it.

Config stores a timeline of every resource. You can ask questions like "show me all the times that S3 bucket policy changed" or "which RDS instances don't have backup retention enabled." You can also write custom Config rules in Lambda to enforce arbitrary constraints.

Enable Config:

bash
aws configservice put-config-recorder \
  --config-recorder-name default \
  --role-arn arn:aws:iam::ACCOUNT:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig

aws configservice start-config-recorder \
  --config-recorder-names default

aws configservice put-delivery-channel \
  --delivery-channel-name default \
  --s3-bucket-name config-bucket-ACCOUNT

The real value emerges when you pair Config with Config Rules. Here's an example that checks S3 buckets for public access:

bash
aws configservice put-config-rule \
  --config-rule '{
    "ConfigRuleName": "s3-bucket-public-read-prohibited",
    "Source": {
      "Owner": "AWS",
      "SourceIdentifier": "S3_BUCKET_PUBLIC_READ_PROHIBITED"
    },
    "Scope": {
      "ComplianceResourceTypes": ["AWS::S3::Bucket"]
    }
  }'

Config Rules evaluate resources on a schedule you define. Non-compliant resources get tagged, logged, and can trigger SNS notifications or Lambda functions for auto-remediation.

Common mistakes

  • Enabling CloudTrail but not enforcing immutability: Set S3 Object Lock on your CloudTrail bucket so logs can't be deleted, even by root. A compromised account is useless if the audit trail is also compromised.
  • Ignoring GuardDuty findings because there are too many: Start by suppressing known false positives (your internal security scanners, your CI/CD agents), then investigate all remaining findings. A suppression rule is two minutes of setup—ignoring findings is career risk.
  • Treating Security Hub as a compliance checkbox: The real value is the continuous monitoring. Review findings weekly, not quarterly. The ones that matter are the ones changing in real-time.
  • Enabling Config but not setting up auto-remediation: Config tells you when something broke. Auto-remediation fixes it before you wake up. For critical rules like "database must not be publicly accessible," auto-remediation is a game-changer.
  • Centralizing logs but not controlling access: Your audit account should be read-only for most engineers. Only your security team should modify CloudTrail or Config settings. Use SCPs (Service Control Policies) to enforce this at the organization level.

Key takeaways

  • CloudTrail records every API call; store logs in a separate, immutable audit account to prevent tampering.
  • GuardDuty ingests CloudTrail and VPC Flow Logs, then runs ML to detect threats in real-time; treat Medium and High findings as blocking issues.
  • Security Hub aggregates findings from multiple services and runs continuous compliance checks against CIS, PCI, HIPAA, and NIST standards.
  • AWS Config tracks infrastructure changes over time and allows you to define rules and auto-remediation for drift.
  • These four services together cost $100–300 per month per account but save you from the $1M+ cost of a breached production system.
  • Enable all four in every account on Day 1, not Day 800 when you've already shipped to production.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles