AWS Production Security Checklist: 10 Controls Before Go-Live
This page is a pre-launch security checklist for teams putting workloads into production on AWS. It covers the eight areas that matter most, from IAM and network setup to detection, patching, and recovery, in the order you should work through them. Use it when launching a new workload, promoting staging to production, or auditing an account before a security review.
Simple explanation
AWS gives you powerful tools but does not configure them for you. The default state of a new AWS account is more permissive than most teams expect: the root account has no MFA, CloudTrail is not enabled, GuardDuty is off, and EBS encryption is not enforced by default.
A security checklist is a list of the controls you need to deliberately turn on before traffic reaches production. It is not about building an impenetrable system. It is about closing the gaps that attackers routinely exploit.
Work from the top of this page down. The controls are ordered by impact. Fixing identity before encryption, and detection before patching, reflects the actual order attackers work through your attack surface.
When to use this checklist
- Launching a new AWS workload. Run through this before the first users hit production.
- Promoting staging to production. Staging environments often have relaxed settings that should not carry over.
- Auditing an existing account. Use each section as an audit prompt to find gaps in a running system.
- Preparing for a security review or compliance conversation. This covers the controls most reviewers check first.
How to use this checklist
Work through the sections in this order. Each area is a dependency for the next.
- Identity first. A compromised AWS identity can undo every other control.
- Network second. Limit what is reachable before worrying about what is encrypted.
- Encryption. Protect data in transit and at rest.
- Secrets. Ensure credentials are not leaking through environment variables or config files.
- Detection. Turn on the logging and alerting you need to know when something goes wrong.
- Patching. Keep the software running your workloads free of known vulnerabilities.
- Deployments and operational access. Secure the pipeline and the humans who operate the system.
- Recovery. Verify you can restore from backup before you need to.
AWS production security checklist
| Control | Why it matters | Minimum AWS service/control | What good looks like |
|---|---|---|---|
| Root account locked down | Root has unrestricted access and cannot be constrained by IAM policies or SCPs | MFA on root account, no access keys | Root never used for daily work; no access keys exist; credentials in a password manager |
| Human access via Identity Center | IAM users with long-lived credentials are a common source of credential theft | AWS IAM Identity Center | Humans log in through Identity Center; no IAM users with console access for new accounts |
| Least-privilege IAM roles on every service | Overpermissioned roles expand blast radius when compromised | IAM roles with specific resource ARNs | Every Lambda, EC2, ECS task has a role scoped to exactly what it needs |
| Private subnets for backend resources | Resources in public subnets are reachable from the internet by default | VPC with public/private subnet split | No EC2 or RDS in public subnets; backend traffic stays inside the VPC |
| Encryption at rest and in transit | Unencrypted data is readable if storage or transport is compromised | ACM for TLS; KMS for at-rest encryption | HTTPS enforced on all external endpoints; RDS, EBS, S3 encrypted at rest |
| Secrets in Secrets Manager | Plaintext secrets in environment variables are visible to anyone with config read access | AWS Secrets Manager or SSM Parameter Store (SecureString) | No plaintext credentials in Lambda env vars, ECS task definitions, or EC2 user data |
| CloudTrail enabled in all regions | API activity is invisible without it; incident investigations become guesswork | Multi-region CloudTrail trail | CloudTrail active in every region; logs in a dedicated S3 bucket with log validation on |
| GuardDuty and Security Hub active | Misconfigurations and active threats go undetected without posture and threat-detection coverage | GuardDuty (all regions); Security Hub | Both services enabled; high-severity findings route to an on-call alert channel |
| Patching or immutable rebuild process | Unpatched software is one of the most common attack vectors after compromised credentials | Systems Manager Patch Manager or image rebuild pipeline | EC2 instances on a patching schedule; container images rebuilt when CVEs are found |
| Backups tested and recovery documented | Untested backups are an untested assumption | AWS Backup or service-native snapshots | Restore completed in a test environment before go-live; RTO and RPO documented |
1. Secure the account and identities
IAM is the first thing to lock down because a compromised AWS identity can bypass every other control on this page.
Think of the root account like a master key that opens every door in the building, including the security room itself. You keep master keys in a safe and use them only when nothing else works. Every other person in the building gets a specific key card that opens only the rooms they actually need. If that key card gets stolen, you replace it and the thief never reaches the server room.
Root account. The root account has unrestricted access to everything and cannot be constrained by IAM policies or Service Control Policies. Lock it down before anything else:
- Enable MFA on the root account
- Remove any access keys (they almost never should exist)
- Store root credentials in a password manager as a break-glass emergency credential
- After initial account setup, never use the root account again for day-to-day work
Root account access keys are the single most dangerous configuration in an AWS account. An exposed root key gives an attacker full, unrestricted control with no way to limit the blast radius. If root access keys exist in your account, delete them immediately.
Human access. For new accounts, AWS IAM Identity Center (formerly SSO) is the recommended approach rather than individual IAM users. Identity Center assigns permission sets to people and connects to identity providers your team already uses (Google Workspace, Okta, Active Directory). Users get short-lived credentials via the AWS access portal. No long-lived IAM user credentials to rotate or leak.
If you are using IAM users in a smaller setup, enforce MFA for all users with console access. An IAM policy condition denying all actions when aws:MultiFactorAuthPresent is false handles this cleanly.
Least privilege for service roles. Every Lambda function, EC2 instance, ECS task, and EKS pod should have an IAM role scoped to the minimum permissions it actually uses. An API that reads from one DynamoDB table should allow exactly dynamodb:GetItem and dynamodb:Query on exactly that table, not dynamodb:* on *.
Write policies with specific resource ARNs rather than wildcards. Review roles quarterly. Permission creep is common and goes unnoticed until a breach.
Temporary credentials via STS. Services should use IAM roles that issue short-lived credentials automatically. Avoid distributing long-lived access keys to applications. If a key leaks, it remains valid until someone manually rotates it.
Multi-account guardrails. If you run more than one AWS account, use AWS Organizations with Service Control Policies to set boundaries no account can override. Common SCPs prevent disabling CloudTrail, restrict which regions workloads can run in, and block creation of public S3 buckets. SCPs apply to all identities in the account, including root users within the Organization.
2. Lock down the network
A well-designed VPC keeps backend resources in private subnets where they are not directly reachable from the internet.
Think of your VPC like an office building. Public subnets are the lobby: anyone can walk in off the street. Private subnets are the back offices: you need a badge to pass the front desk. The database tier is the server room: only specific staff with a separate clearance card can enter. An attacker who manages to get into the lobby still cannot walk straight into the server room.
Subnet architecture. Use at least two subnet tiers:
- Public subnets. Only resources that must be internet-accessible, specifically Application Load Balancers and NAT Gateways. Even EC2 instances serving web traffic should sit in private subnets behind an ALB, not in public subnets with direct internet exposure.
- Private subnets. Application servers, RDS databases, ElastiCache, Lambda functions in a VPC, ECS tasks. No inbound internet access; outbound through a NAT Gateway if needed.
- Database subnets (recommended). A dedicated tier for RDS and ElastiCache with no internet route. Only the application subnet should reach it.
Admin access. Replace SSH over port 22 with AWS Systems Manager Session Manager. Session Manager requires no open inbound port. Instances connect outbound to the Systems Manager service endpoint. There is no bastion host to maintain, no SSH keys to distribute, and every session is logged in CloudTrail.
Security groups are stateful and default-deny. They are the primary network control at the resource level:
- Database security groups should reference the application security group as the allowed source, not a CIDR range. This ensures only instances in the application tier can reach the database, regardless of IP changes.
- Never use
0.0.0.0/0as a source except for the ALB’s HTTP/HTTPS inbound rule. - Tighten outbound rules where possible. Most backend services do not need unrestricted outbound access.
Security Groups vs NACLs:
| Security Groups | NACLs | |
|---|---|---|
| Operates at | Resource (instance, RDS, Lambda in VPC) | Subnet |
| Stateful | Yes, return traffic is automatic | No, both directions must be explicitly allowed |
| Default behaviour | Deny all inbound, allow all outbound | Allow all (default NACL) |
| Rule evaluation | All rules evaluated; most permissive wins | Evaluated in order; first match wins |
| Best use | Primary traffic control per resource | Secondary layer; blocking specific IP ranges |
For most workloads, well-configured security groups are sufficient. Network ACLs add value as a secondary layer to block known-bad IP ranges or as a defence-in-depth measure.
3. Encrypt data in transit and at rest
In transit. All external traffic should use TLS 1.2 or higher. AWS Certificate Manager (ACM) provides free TLS certificates for Application Load Balancers and CloudFront distributions. Configure your ALB to redirect HTTP to HTTPS automatically. For internal service-to-service traffic that crosses network or process boundaries, use TLS where practical.
At rest. Most AWS managed services support encryption at rest. Verify these before go-live:
- RDS. Enable encryption at creation time. It cannot be enabled after the fact. You would need to take a snapshot and restore an encrypted copy, which involves downtime.
- S3. SSE-S3 encryption is on by default for all new objects. No action required unless you need customer-managed keys.
- EBS volumes. Enable EBS encryption by default at the region level in the EC2 console so every new volume is automatically encrypted.
- DynamoDB. Encrypted at rest by default using AWS-managed keys.
AWS-managed keys vs customer-managed keys (CMKs). AWS-managed keys are the right default for most workloads. Customer-managed KMS keys give you an audit trail (every encrypt/decrypt logged in CloudTrail) and the ability to revoke access by disabling the key. CMKs add operational overhead, including key rotation policy, access policy management, and key deletion windows. Adopt them when the compliance or control requirement is real, not hypothetical. See Customer-Managed Keys for a practical breakdown.
Not sure whether you need customer-managed KMS keys? Ask whether your security or compliance team needs to audit who decrypted which specific files and when. If yes, use SSE-KMS with a CMK. If that question does not apply to your workload, SSE-S3 or AWS-managed keys are the right default and cost nothing extra.
For a broader view of encryption across AWS services, including how RDS, EBS, and S3 handle keys differently, that page covers the full picture.
4. Manage secrets safely
Database passwords, API keys, OAuth client secrets, and private certificates should never appear in:
- Lambda environment variables
- ECS task definition environment variables
- EC2 user data scripts
- Application config files committed to Git
- Hardcoded in source code
Lambda environment variables are visible in plaintext in the AWS console to anyone with lambda:GetFunctionConfiguration permission. ECS task definition environment variables are stored in plaintext in the task definition. This is not a misconfiguration in your IAM policies — it is how the service works. Anyone with read access to the service config can read every “secret” stored there.
The correct approach is AWS Secrets Manager. Store the secret there, grant the Lambda execution role or ECS task role permission to read it, and retrieve it at runtime. The plaintext value never touches environment configuration.
const { SecretsManagerClient, GetSecretValueCommand } = require('@aws-sdk/client-secrets-manager');
const client = new SecretsManagerClient({ region: 'us-east-1' });
async function getDatabaseCredentials() {
const response = await client.send(new GetSecretValueCommand({
SecretId: 'prod/myapp/db-credentials'
}));
return JSON.parse(response.SecretString);
}Secrets Manager also handles automatic rotation for RDS credentials. It rotates the password on a schedule and updates the secret value without application downtime.
Secrets Manager vs SSM Parameter Store:
| Secrets Manager | SSM Parameter Store (SecureString) | |
|---|---|---|
| Cost | ~$0.40/secret/month | Free up to 10,000 standard parameters |
| Automatic rotation | Built-in for RDS, Redshift, and custom Lambda | Not built-in; requires custom rotation logic |
| Cross-account access | Easy via resource policy | Possible but more complex |
| Best for | Database credentials and API keys needing rotation | Config values and non-rotating sensitive strings |
For most production secrets, Secrets Manager is the right choice. For config values that are sensitive but do not rotate, SSM Parameter Store is lighter and cheaper.
For CI/CD pipelines, see Secrets in CI/CD Pipelines. Build systems have different exposure surfaces than application runtime environments.
5. Turn on logging, detection, and posture checks
Securing infrastructure is necessary but not sufficient. You also need to know when something goes wrong.
CloudTrail is security camera footage for your AWS account. It records who walked in, when, and what they touched. GuardDuty is a security analyst watching the footage live and raising an alarm when something looks wrong. Security Hub is the manager’s daily report: a ranked list of every open issue across all the cameras, sorted by severity. You need all three for a complete picture.
CloudTrail records every API call made in your AWS account: who made it, when, from where, with what parameters, and whether it succeeded. Enable a multi-region trail so activity in every region is captured from day one. Send logs to an S3 bucket in a dedicated logging account. That way, even if an attacker compromises your main account, the audit trail is intact. Enable log file validation to detect tampering.
CloudTrail is the foundation of every incident investigation: “who created this resource?”, “when were these credentials used?”, “who changed this policy?” Without it, you are reconstructing events from memory. See CloudTrail log types for the difference between management events and data events.
GuardDuty uses machine learning to detect suspicious activity by analysing CloudTrail, VPC Flow Logs, and DNS query logs. It generates findings for events like an EC2 instance communicating with a known command-and-control server, unusual API calls from a new geographic location, or cryptocurrency mining activity. Enable GuardDuty in every region, every account. It costs a few dollars per month for most accounts, which is small relative to what it detects.
Route high-severity GuardDuty findings to an SNS topic connected to your on-call alerting channel. A finding that sits unreviewed for weeks provides no protection at all.
Many teams enable GuardDuty, see a batch of findings appear on day one, decide they look like noise, and never review them again. GuardDuty findings are not generic alerts. Each one references a specific resource, a specific action, and a specific time. Review the first batch carefully to understand what is normal for your account before deciding anything is noise.
Security Hub aggregates findings from GuardDuty, AWS Inspector, IAM Access Analyzer, and other services into one view. It also runs the AWS Foundational Security Best Practices standard, which checks your account for misconfigurations like public S3 buckets, unencrypted RDS instances, or IAM access keys that have not been rotated. Use Security Hub findings as a prioritised remediation list. Review it weekly during the first months after launch.
AWS Inspector scans your EC2 instances and ECR container images continuously for known CVEs. Enable it for both EC2 and ECR. It runs in the background and surfaces vulnerabilities in your deployed software without requiring manual scans.
6. Patch, scan, and rebuild workloads
Unpatched software is one of the most common attack paths after credential compromise.
Managed services (RDS, ElastiCache, ECS Fargate, Lambda). AWS patches the underlying infrastructure. For RDS, enable auto minor version upgrades so security patches apply automatically. Have a tested plan for major version upgrades. They require manual scheduling but carry security fixes that do not reach minor versions.
EC2 instances. Use AWS Systems Manager Patch Manager to automate patching. Define a patch baseline (which patches to apply), a maintenance window (when to apply them), and patch groups (which instances are in scope). Test patches in staging before production.
Container images. When Inspector’s ECR scanning finds a CVE in a deployed image, rebuild the image from an updated base and redeploy. Do not patch running containers in place. Rebuild from source so the image stays reproducible and every instance starts from a known state.
The patching burden is highest for EC2 workloads that own their operating system. For ECS Fargate and Lambda, AWS manages the underlying host. If Inspector is flagging your EC2 instances frequently, that is a useful signal to consider whether a managed runtime would reduce your patching surface.
Immutable infrastructure. For EC2, the cleaner approach is immutable replacement: build a new AMI with the patch applied, deploy new instances from it, and terminate the old ones. This eliminates configuration drift and removes the “what did someone change on this running instance” problem. It costs more to automate up front but is operationally simpler over time.
7. Secure deployments and operational access
A CI/CD pipeline with excessive permissions is a privileged path straight into your production account.
CI/CD roles. The IAM role your pipeline uses to deploy should have only the permissions that deployment actually requires, not AdministratorAccess. Use a dedicated role for each pipeline stage (build, test, deploy to staging, deploy to production), scoped to the minimum. See Secure CI/CD Pipelines for scoping patterns.
No long-lived access keys in pipelines. If your CI/CD platform supports OIDC federation, use it. The pipeline exchanges an OIDC token for temporary credentials from STS. No stored access keys to rotate or accidentally leak in build logs.
GitHub Actions supports OIDC federation with AWS out of the box. With about 10 lines of workflow configuration you can give your pipeline temporary credentials without storing any long-lived access keys in GitHub Secrets. If your pipeline currently uses IAM access keys, this migration is worth doing before go-live.
Operational access via Session Manager. For production debugging, use Session Manager rather than SSH. It requires no inbound port 22, no bastion host, and no SSH key distribution. Every session is logged to CloudTrail and optionally to S3 and CloudWatch Logs. Access is controlled by IAM, so you can scope who can start a session on which instances.
Break-glass access. Define and document a procedure for emergency access when normal controls are unavailable. This typically means a dedicated break-glass IAM role in each account with broad emergency permissions, protected by a separate MFA device, with CloudTrail alerts configured for every assumption of that role. The goal is that break-glass use is always detected, reviewed, and time-limited.
8. Backups and recovery readiness
A backup you have never restored is an untested assumption.
What to back up. At minimum: RDS automated snapshots with a retention period that matches your RPO, EBS snapshots for stateful workloads, S3 versioning on buckets where data loss is unacceptable, and DynamoDB with point-in-time recovery (PITR) enabled. AWS Backup can centralise backup policy across services into one place.
Test a restore before go-live. Before production launch, restore at least one backup into a test environment and verify both the data is intact and the application works against it. This sounds obvious, but most teams skip it until after an incident forces it.
Most teams discover their restore procedure is broken during an actual incident, not a drill. RDS snapshot restores work differently from point-in-time recovery. A database dump is not the same as a snapshot restore. Test the specific restore path you will actually use in production, not a generic backup check.
Recovery objectives. Document your RTO (how long you can tolerate being down) and RPO (how much data loss is acceptable). These numbers drive decisions about backup frequency, cross-region replication, and whether you need a warm standby or active/active setup. See Disaster Recovery Strategies for the four DR patterns and when each makes sense.
Cross-region backups. For workloads where a regional failure would be unacceptable, copy snapshots to a second region. RDS snapshots can be copied cross-region manually or via Lambda automation. S3 Cross-Region Replication handles objects automatically. The goal is that a region going down does not also destroy your most recent recovery point.
Common mistakes
- Using root account credentials in CI/CD pipelines. Teams often wire up a pipeline with root credentials to get things working quickly and then never change it. A compromised pipeline with root credentials means full, unrestricted account access. Create a dedicated IAM role for each pipeline with only what the deployment needs.
- Security groups with open inbound rules that never get tightened. Port 22 open to
0.0.0.0/0for now, or a database security group allowing any CIDR. Audit all security group inbound rules before go-live. Use Session Manager instead of SSH. It requires no open inbound port at all. - Plaintext secrets in environment variables. The Lambda console shows environment variables to anyone with
lambda:GetFunctionConfiguration. ECS task definitions store them in plaintext. Use Secrets Manager and retrieve at runtime via the SDK. - Enabling GuardDuty and then ignoring the findings. GuardDuty findings are useless if nobody acts on them. Route high-severity findings to SNS connected to your on-call alerting. Review medium-severity findings on a weekly schedule. A finding that sits unread for months is the same as no detection capability.
- RDS encryption not enabled at creation. RDS encryption must be enabled when the database is created. You cannot turn it on after the fact without taking a snapshot and restoring into an encrypted instance, which involves downtime. Enable it from day one.
- No tested restore procedure. Most teams have backups configured. Few have a tested, documented restore procedure. Run a restore drill in a test environment before go-live. The first restore should never happen during an actual incident.
Summary
- Lock the root account first (MFA, no access keys, no daily use), then enforce least-privilege IAM roles for every service. Not broad wildcards.
- Put backend resources in private subnets; use security groups with specific group or CIDR sources rather than
0.0.0.0/0, and replace SSH with Session Manager. - Encrypt data at rest and in transit. RDS, S3, EBS, and DynamoDB all support it; ACM handles TLS certificates at no cost for load balancers and CloudFront.
- Store credentials in Secrets Manager, not in Lambda env vars, ECS task definitions, or EC2 user data, and let it handle rotation.
- Enable CloudTrail (multi-region), GuardDuty (all regions), and Security Hub, then route findings to alerting so they are actually acted on.
- Test a backup restore before go-live. An untested restore procedure is not a recovery plan.
Frequently asked questions
What should I secure first in a new AWS production account?
Start with the root account: enable MFA, remove access keys, and stop using it for daily work. Next, enable CloudTrail in all regions so you have an audit trail from day one. Then lock down IAM — either set up IAM Identity Center for human access or enforce MFA on IAM users, and apply least-privilege roles to every service. These three steps cover the most common attack vectors and take under a day to complete.
Do I need both GuardDuty and Security Hub?
Yes — they do different things. GuardDuty actively monitors CloudTrail, VPC Flow Logs, and DNS logs to detect suspicious behaviour in real time (unusual API calls, compromised credentials, EC2 instances talking to known malicious IPs). Security Hub aggregates findings from GuardDuty and other services, runs configuration checks against AWS best practice standards, and gives you a prioritised remediation list. GuardDuty finds active threats; Security Hub finds misconfigurations. Use both.
Should secrets ever be stored in Lambda or ECS environment variables?
Not as plaintext values. Lambda environment variables are visible in the AWS console to anyone with lambda:GetFunctionConfiguration permission. ECS task definition environment variables are stored in plaintext in the task definition. The correct approach is to store the secret in AWS Secrets Manager or SSM Parameter Store (SecureString type) and retrieve it at runtime using the AWS SDK. A reference to a Secrets Manager ARN in your configuration is fine — the secret value itself should never be the plaintext entry.
What is the difference between security groups and NACLs?
Security groups operate at the resource level (EC2 instance, RDS instance, Lambda in a VPC) and are stateful — if you allow inbound traffic, the return traffic is automatically allowed. Network ACLs (NACLs) operate at the subnet level and are stateless — you must explicitly allow both inbound and outbound directions for any flow. Security groups are the primary control for most workloads. NACLs are a secondary layer used to explicitly block specific IP ranges or as a defence-in-depth measure. Most teams configure security groups carefully and leave NACLs at their default (allow all).
When do I need customer-managed KMS keys?
Use customer-managed keys (CMKs) when you need an audit trail of who decrypted what and when (CloudTrail logs every KMS API call for CMKs), when you need to revoke access to data by disabling the key, or when a compliance framework explicitly requires key separation from the storage service. For most workloads, AWS-managed keys are sufficient. CMKs add operational complexity — you manage rotation, access policies, and key deletion — so only adopt them when a real compliance or control requirement exists.