# AWS Security Survival Kit: thirty-one detections and an account hardening pass

> The AWS Security Survival Kit is a Makefile and two CloudFormation templates that turn a bare AWS account into one that tells you when something bad happens, on the assumption that CloudTrail gives you the logs and GuardDuty covers a narrow slice, and that neither tells you when the root account signs in or somebody stops CloudTrail. The make deploy target does three things in order: harden the account across every enabled region, deploy a local and a global stack, and then turn on termination protection so the alerting cannot be deleted by the same credentials it watches.

**zoph-io/aws-security-survival-kit** — Bare minimum AWS Security Alerting and Secure by default Configuration

- Repository: https://github.com/zoph-io/aws-security-survival-kit
- Website: https://zoph.io
- Stars: 525 · Forks: 43
- Language: Makefile
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/zoph-io-aws-security-survival-kit

## What it is, and the size of the promise

The AWS Security Survival Kit is the smallest possible AWS account monitoring kit. Two CloudFormation stacks, about thirty EventBridge rules, an SNS topic. You get an email, or Slack or Teams through AWS Chatbot, the moment something interesting happens in your account. Free or near-free, no agents, no extra accounts, no SaaS.

The scope statement is at the end of the Why section and it is worth reading literally. ASSK is the minimum so that the idea that you would notice within minutes if something obviously bad happens is true for your AWS account. It complements GuardDuty. It is not a SIEM, a SOAR, or a full detection engineering practice.

That last sentence is the author being careful, and it is a good sign. Most alerting bundles would claim to replace GuardDuty. This one says it is a floor.

The primary language is Makefile, which is the whole point, and the licence is Apache-2.0, the default branch is main, and the homepage is zoph.io. The last push was on 2026-09-20 with the project not archived. The recent releases are v1.1.0 from 2026-06-27, with a description that lists more detections, full dashboards, SSM hardening, SCP docs and plain-text alerts, and v1.0.0 from 2026-06-14.

So the project started in June 2026 and has had one substantial release since. That is consistent with a focused piece of infrastructure rather than a long-running framework.

The repository layout matches the promise. There is a Makefile, cfn-global.yml and cfn-local.yml for the two CloudFormation templates, a README, assets/, an AGENT.md, a .gitignore and a LICENSE. Two templates, one makefile, and a small amount of documentation. There is no application code because there is no application: the whole thing is a deployment.

## The gap it fills, in the author's own list

The Why section starts with a fair observation. AWS gives you excellent logging with CloudTrail, VPC Flow Logs and Config, but very few alerts, and GuardDuty covers a narrow slice with threat intelligence and anomaly detection on a few services. What it does not tell you is on a list, and the list is the clearest statement of the problem.

The root account just logged in. Someone disabled CloudTrail, deleted a KMS key, or stopped your GuardDuty detector. A new IAM user was created, or AdministratorAccess was attached to a role. A security group was opened on port 22 to 0.0.0.0/0. A snapshot or AMI was shared with another AWS account. A flood of AccessDenied errors is happening, which could be enumeration, a misconfigured workload or a compromised credential being probed. And your own monitoring stack just got deleted.

Six items, and they are not arbitrary. Four of them are about the account's own security controls being switched off, which is the classic AWS attack pattern: a compromised credential disables the logging, then the trail is gone and nobody knows. Two are about privilege escalation, one is about network exposure, one is about data leaving the account, and one is about credential probing.

That last one, the AccessDenied flood, is a good example of the project's judgement. A single AccessDenied is noise, because everything in AWS generates them. A burst is a signal, because it means something is trying things it should not be able to. So the detection is a CloudWatch alarm on a rate, not a rule on an event, and the README notes the threshold is configurable through the parameters.

The comparison to make is with the cost of not having this. The alternative to thirty-one EventBridge rules and an SNS topic is reading CloudTrail by hand, which nobody does, or running a full detection engineering practice, which most teams cannot staff. The middle path is what this is.

## Deploying it, and the three things the target does

The quick start is two steps, and the first one is where you actually make the decisions.

Edit the variables at the top of the Makefile, which are AlarmRecipient, Project, LocalAWSRegion and CTLogGroupName. Then deploy:

```bash
make deploy
```

And the README then explains what make deploy does, in three things, in order.

First it hardens the account, across every enabled region: S3 Block Public Access, EBS encryption, a public AMI and snapshot block, IMDSv2 as the default, and an SSM document public-sharing block. Second it deploys both CloudFormation stacks, the local one in your region and the global one in us-east-1. Third it enables CloudFormation stack termination protection on both stacks.

That third step is the one that makes the rest work, and it is why the two stacks are separate. The kit monitors the account, and the natural way to defeat an alerting system is to delete the thing that sends the alerts. Termination protection prevents that, and it is applied by the same command that installs the monitoring, so you cannot accidentally deploy the kit without it.

The tear-down target is the mirror image: it disables termination protection and deletes both stacks. Which means you cannot clean up without knowing that the protection is the first thing to go.

The two-stack split follows from how AWS works. The local stack holds what applies in one region, and the global one in us-east-1 holds what is account-wide. Some AWS services only report to the us-east-1 trail, and some resources only exist in some regions, so a single stack in a single region would have blind spots.

The README also frames the promise in terms of what you can expect. The whole point is that the idea of noticing within minutes if something obviously bad happens becomes true, and the kit is designed to be the floor under that rather than a complete practice.

## The parameters, and the ones that exist because something broke

The parameter section of the Makefile is where the practical experience shows, because several of the settings are there to solve a specific failure.

The defaults are:

```bash
AlarmRecipient ?= hello@zoph.io
Project ?= aws-security-survival-kit
Description ?= Bare minimum AWS Security alerting and configuration
LocalAWSRegion ?= eu-west-1
CTLogGroupName ?= aws-cloudtrail-logs-567589703415-c7b72250
```

So the alarm recipient defaults to an address at the author's own domain, which is a placeholder you must change, the project name defaults to aws-security-survival-kit, the description is Bare minimum AWS Security alerting and configuration, the local region defaults to eu-west-1, and the CloudTrail log group is a specific account's log group.

That last default is worth pausing on. CTLogGroupName is set to a log group name that contains a specific AWS account number, so a fresh deployment in a different account needs it changed, and the fact that it is a parameter rather than something derived is a small piece of friction that a reader should notice.

The profile handling is more careful:

```bash
Profile ?=
PROFILE_FLAG := $(if $(strip $(Profile)),--profile $(Profile),)
```

The comment explains it. Pass Profile with the name of an AWS CLI profile, and leave it empty, the default, when the credentials come from the environment, for example from aws-vault exec, an IAM role or an ECS task role. In that case the --profile flag is omitted entirely. The Makefile then computes the flag conditionally, so you get a working command in both cases rather than an empty argument.

Then the skip list for regions:

```bash
# that consistently 5xx (e.g. me-south-1 has had EC2 control-plane issues).
# Example: make deploy SkipRegions="me-south-1 ap-east-2"
```

A space-separated list of regions to skip during the account-level hardening step, for regions that are currently down, that you do not use, or that consistently return 5xx, and the example given is me-south-1 having EC2 control-plane issues. And the timeout settings:

```bash
# 60s per call x 4 calls per region). 10s connect / 15s read fails fast.
AwsCliTimeouts := --cli-connect-timeout 10 --cli-read-timeout 15
```

The reason is stated as a calculation. Without this, a single unreachable region can stall the hardening loop for several minutes, since the default AWS CLI socket timeout is 60 seconds per call times four calls per region. Ten seconds connect and fifteen seconds read fails fast. That is a small change to a makefile and the difference between a deploy that takes a minute and one that appears to hang.

Finally the staging bucket:

```bash
# Leave empty to attempt inline deploy. If your account has CFN's default
# managed bucket, it is named cf-templates-<random>-<region> - run
# `aws s3 ls | grep cf-templates` to find yours, or create one.
LocalS3Bucket  ?=
```

It is required once the templates grow past 51,200 bytes, the CloudFormation inline limit, and you leave it empty to attempt an inline deploy. If your account has the default managed bucket, it is named cf-templates with a random suffix and region, and the comment tells you how to find yours.

## The detection list, and the noise control

The always-on detections are numbered to thirty-one, and reading them is the fastest way to understand what the kit covers.

They start with the account-level events: root user activity, CloudTrail tampering through StopLogging, DeleteTrail and UpdateTrail, and AWS Health Dashboard events. Then IAM, split into user changes, admin escalation through attaching a policy with AdministratorAccess, and MFA device changes. Then two CloudWatch alarms rather than rules, an AccessDenied and UnauthorizedOperation burst with a configurable threshold, and console login failures.

Then the data exfiltration set: EBS snapshot exfiltration through ModifySnapshotAttribute and the shared snapshot events, AMI exfiltration through ModifyImageAttribute, and CloudShell exfiltration through GetFileDownloadUrls. Then the control-plane tampering set: KMS key tampering, security group ingress and egress changes, and AWS Config tampering through the configuration recorder and config rule deletions. Then the monitoring-evasion set: Route53 Resolver query-log deletion, VPC Flow Logs deletion and modification, and GuardDuty tampering including deleting the detector and stopping monitoring members.

Then exposure: security group admin-port exposure for IPv4 with ports 22 and 3389 from 0.0.0.0/0, and the same for IPv6 with ::/0. Then IAM Roles Anywhere profile and trust anchor creation, and STS GetFederationToken. Then self-protection on the kit's own stacks, detecting DeleteStack and UpdateTerminationProtection. Then a public Lambda function URL with AuthType NONE, S3 bucket replication configuration, a CloudWatch Logs bulk export task, an RDS instance made publicly accessible, and an EC2 Client VPN endpoint creation.

The self-protection detection is the one that closes the loop. The kit watches for deletion of the stacks that do the watching.

Then there are two other lists, and they are the most unusual part of the documentation.

The opt-in detections are off by default, and the reason is given: they are noisy in infrastructure-as-code-heavy environments or only meaningful in specific account and region contexts. Each one has a parameter and an enable-when condition. S3 bucket policy and ACL changes, Lambda creation and code updates, IAM Identity Center permission sets, VPC peering and routes and internet gateways, IAM UpdateAssumeRolePolicy trust policy changes, and two kinds of AWS Organizations detection, one for tampering and one for membership changes.

That Organizations membership entry has a detail that is worth reading twice. The Cleaned event can arrive about ninety days after a CloseAccount, so the detection is described as very low volume but also late.

And the third list is the one that shows the author has run this in production. Three existing detections stay on for backward compatibility, but they are called alert-fatigue cannons in active accounts, and the instruction is to flip the parameter to false if your inbox is drowning. sts:GetCallerIdentity is called on every AWS SDK init, every terraform plan and every CI step. secretsmanager:BatchGetSecretValue is called by ECS, EKS and Lambda when batch-loading secrets at startup. And ec2:GetPasswordData is called by SSM and admin tooling on every Windows admin password fetch.

A kit that tells you to turn off three of its own alerts is a kit written by somebody who has read their own inbox.

## How to evaluate it, and what to check first

There are five things to consider, and the first is that this is a Makefile rather than a tool, which changes how you adopt it.

There is no CLI to install and no package to depend on. You clone the repository, edit four variables at the top of the Makefile, and run make deploy. What you get is two CloudFormation templates, which means you can read them before you run them, and you can copy them into your own deployment pipeline if that is how you work. The tradeoff is that there is no version pinning or upgrade mechanism beyond taking a new release of the templates, and the templates are the artefact, not the makefile.

Second, the blast radius of make deploy is worth understanding before you run it. It hardens the account across every enabled region, which means turning on S3 Block Public Access account-wide, defaulting EBS encryption, blocking public AMIs and snapshots, making IMDSv2 the default, and blocking public sharing of an SSM document. Those are changes that can break existing workloads, particularly public S3 buckets and AMIs that other accounts depend on. The SkipRegions parameter and the per-region loop are there to make the operation survivable, but you should read the hardening list before running it against an account with production in it.

Third, the CloudTrail log group parameter is a specific account's log group, so a fresh deployment in another account needs it changed. A misconfigured log group means the EventBridge rules have nothing to match against, and the kit would be silently inert.

Fourth, the alert delivery. The channel is SNS, and the options are email, or Slack and Teams through AWS Chatbot. The emergency account access scenario is the one to test first, because a kit that does not reach you when the root account signs in is not a kit.

Fifth, the opt-in and opt-out lists. The defaults are deliberately conservative in both directions, which is the right choice, but it means the kit as deployed does not cover S3 access-surface changes, Lambda changes, SSO permission sets or network infrastructure changes. Read both tables and decide what your account needs, because a detection you have not enabled is a detection you do not have, and three of the default-on ones will drown you if your account is busy.

What you get is a floor under AWS monitoring that you can read in an afternoon, apply in a minute, and take away when you have built something better. The cost is that it makes real changes to your account as a side effect of installing it, and that it assumes the defaults of an account that is not running much.

## Conclusion

Adopt the AWS Security Survival Kit if you have an AWS account with no meaningful alerting and want a floor under it, because the CloudTrail and GuardDuty coverage people rely on does not include the case of somebody switching off your own logging. Do not run make deploy against an account with production in it without reading the hardening list first, since it turns on account-wide S3 Block Public Access, default EBS encryption, an IMDSv2 default and public sharing blocks, and any of those can break a workload that depended on the old setting. Before you commit, change the four variables the README names, including the CloudTrail log group, which is set to a specific account and would otherwise leave every rule matching against nothing, and test the root account sign-in alert, since a kit that does not reach you when it matters is not a kit.

## FAQ

### What is the AWS Security Survival Kit?

It is the smallest possible AWS account monitoring kit: two CloudFormation stacks, around thirty EventBridge rules and an SNS topic, with no agents, no extra accounts and no SaaS. It is MIT-style Apache-2.0 licensed, the primary language is Makefile, and the last push to the repository was on 2026-09-20 with v1.1.0 the most recent release.

### What does make deploy actually do?

Three things in order. It hardens the account across every enabled region, applying S3 Block Public Access, EBS encryption, a public AMI and snapshot block, IMDSv2 as the default and an SSM document public-sharing block. It then deploys both CloudFormation stacks, the local one in your region and the global one in us-east-1. Finally it enables termination protection on both stacks so the alerting cannot simply be deleted.

### Which security events does the kit detect by default?

Thirty-one always-on detections including root user activity, CloudTrail tampering, IAM user and admin escalation and MFA changes, an AccessDenied burst alarm, console login failures, EBS snapshot and AMI exfiltration, KMS key tampering, security group changes, AWS Config and GuardDuty tampering, Route53 and VPC Flow Log deletion, admin port exposure on IPv4 and IPv6, a public Lambda function URL, an RDS instance made public and a self-protection rule on the kit's own stacks.

### Why might I want to turn some detections off?

Three default-on detections are described as alert-fatigue cannons in active accounts: sts:GetCallerIdentity, which is called on every AWS SDK init, terraform plan and CI step, secretsmanager:BatchGetSecretValue, called by ECS, EKS and Lambda at startup, and ec2:GetPasswordData, called by SSM and admin tooling. The instruction is to flip the parameter to false if your inbox is drowning.

### What are the Makefile parameters I need to set before deploying?

The README asks you to edit AlarmRecipient, Project, LocalAWSRegion and CTLogGroupName. The Makefile also supports a Profile variable for a named AWS CLI profile, a SkipRegions list for regions that are down or unused, per-call timeouts of 10 seconds connect and 15 seconds read, and an optional S3 bucket for staging templates once they exceed the 51,200 byte CloudFormation inline limit.

## Sources

- [License: Apache-2.0](https://github.com/zoph-io/aws-security-survival-kit/blob/main/LICENSE)
- [Project website](https://zoph.io)
- [README](https://github.com/zoph-io/aws-security-survival-kit/blob/main/README.md)
- [Releases](https://github.com/zoph-io/aws-security-survival-kit/releases)
- [zoph-io/aws-security-survival-kit on GitHub](https://github.com/zoph-io/aws-security-survival-kit)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/zoph-io-aws-security-survival-kit
