AWS Well-Architected Review — automated assessment and PDF report generator
Project description
AWS WAR Lens
Copyright (c) 2025 BluNorth Technologies Inc. All rights reserved. See LICENSE for terms.
Automated AWS Well-Architected Review assessments. Scans your AWS account across all six pillars, produces risk-rated findings, and generates a PDF report — with optional LLM-powered narrative and prioritization.
Features
- 175 programmatic checks across all 6 Well-Architected pillars
- Risk-rated findings — CRITICAL / HIGH / MEDIUM / LOW / PASS
- PDF report with executive summary, top priorities, and cross-finding correlations
- Multi-region scanning —
--region allscans every opted-in region in parallel - No infrastructure required — runs locally or in Docker against any AWS account
Pillars
| Pillar | Checks |
|---|---|
| Security | 61 |
| Reliability | 44 |
| Operational Excellence | 34 |
| Performance Efficiency | 14 |
| Sustainability | 11 |
| Cost Optimization | 11 |
Installation
pip install aws-war-lens
Note: WeasyPrint (used for PDF generation) requires native system libraries on Linux/macOS. See WeasyPrint installation docs if you hit dependency errors.
Usage
# Scan a single region (security pillar by default)
aws-war-lens --region eu-west-1
# Scan multiple pillars
aws-war-lens --region eu-west-1 --pillars security reliability cost_optimization
# Scan all opted-in regions
aws-war-lens --region all
# Skip LLM analysis (no ANTHROPIC_API_KEY needed)
aws-war-lens --region eu-west-1 --no-llm
# Use a named AWS profile
aws-war-lens --region eu-west-1 --profile myprofile
# Use explicit credentials
aws-war-lens --region eu-west-1 --access-key AKIA... --secret-key xxxx
The PDF report is saved to output/ by default. Use --output <dir> to change it.
Authentication
Credentials are resolved in this order:
--access-key/--secret-key/--session-tokenflags--profilenamed profile from~/.aws/credentialsAWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEYenvironment variables- Default AWS CLI profile
- IAM instance profile / ECS task role / Lambda execution role
LLM Analysis
Set ANTHROPIC_API_KEY in your environment (or a .env file) to enable LLM-powered narrative, executive summary, and cross-finding correlation in the report. Without it, the report renders with raw findings only.
export ANTHROPIC_API_KEY=sk-ant-...
aws-war-lens --region eu-west-1
Docker
docker run --rm \
-e ANTHROPIC_API_KEY=sk-ant-... \
-e AWS_ACCESS_KEY_ID=... \
-e AWS_SECRET_ACCESS_KEY=... \
-e AWS_SESSION_TOKEN=... \
-v "$(pwd)/output:/app/output" \
aws-war-lens --region eu-west-1
Available Pillars
| Value | Description |
|---|---|
security |
IAM, S3, GuardDuty, CloudTrail, KMS, VPC, ACM, and more |
reliability |
Auto Scaling, RDS multi-AZ, backups, Route 53, quotas |
performance |
Instance families, Graviton, DynamoDB, CloudFront, ElastiCache |
cost_optimization |
Idle resources, rightsizing, reserved capacity, orphaned snapshots |
operational_excellence |
CloudWatch alarms, SSM, tagging, CI/CD, ECS configuration |
sustainability |
Graviton adoption, Fargate, auto-scaling, S3 intelligent tiering |
IAM Permissions
Option 1 — Recommended (least privilege): Create a dedicated IAM policy using the JSON provided below and attach it to a new user or role.
AWS Console: IAM → Policies → Create policy → JSON tab → paste the policy below → name it AWSScannerReadOnly
AWS CLI:
aws iam create-policy \
--policy-name AWSScannerReadOnly \
--policy-document file://policy.json
Option 2 — Use existing credentials: If the credentials you already have (admin, power user, or an existing role) include at least the permissions listed below, you can use them directly without creating a new policy.
Copy IAM policy JSON
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "STSAuthentication",
"Effect": "Allow",
"Action": ["sts:GetCallerIdentity"],
"Resource": "*"
},
{
"Sid": "EC2ReadOnly",
"Effect": "Allow",
"Action": [
"ec2:DescribeRegions", "ec2:DescribeInstances", "ec2:DescribeSecurityGroups",
"ec2:DescribeVolumes", "ec2:DescribeSnapshots", "ec2:DescribeImages",
"ec2:DescribeAddresses", "ec2:GetEbsEncryptionByDefault", "ec2:DescribeVpcs",
"ec2:DescribeSubnets", "ec2:DescribeNetworkAcls", "ec2:DescribeVpcEndpoints",
"ec2:DescribeFlowLogs", "ec2:DescribeNatGateways", "ec2:DescribePlacementGroups"
],
"Resource": "*"
},
{
"Sid": "IAMReadOnly",
"Effect": "Allow",
"Action": [
"iam:GetAccountSummary", "iam:GetAccountPasswordPolicy", "iam:GetCredentialReport",
"iam:GenerateCredentialReport", "iam:GetRole", "iam:ListUsers",
"iam:ListAttachedUserPolicies", "iam:ListUserPolicies", "iam:ListRoles",
"iam:ListPolicies", "iam:GetPolicyVersion"
],
"Resource": "*"
},
{
"Sid": "S3ReadOnly",
"Effect": "Allow",
"Action": [
"s3:ListAllMyBuckets", "s3:GetAccountPublicAccessBlock", "s3:GetPublicAccessBlock",
"s3:GetBucketAcl", "s3:GetBucketPolicy", "s3:GetEncryptionConfiguration",
"s3:GetBucketVersioning", "s3:GetBucketLogging", "s3:GetLifecycleConfiguration",
"s3:GetReplicationConfiguration", "s3:GetBucketObjectLockConfiguration",
"s3:GetBucketTagging", "s3:GetBucketAccelerateConfiguration",
"s3:ListBucketIntelligentTieringConfigurations", "s3:ListBucket"
],
"Resource": "*"
},
{
"Sid": "RDSReadOnly",
"Effect": "Allow",
"Action": [
"rds:DescribeDBInstances", "rds:DescribeDBSnapshots", "rds:DescribeDBClusters",
"rds:DescribeReservedDBInstances", "rds:DescribeDBProxies"
],
"Resource": "*"
},
{
"Sid": "ECSReadOnly",
"Effect": "Allow",
"Action": [
"ecs:ListClusters", "ecs:DescribeClusters", "ecs:ListServices",
"ecs:DescribeServices", "ecs:ListTaskDefinitionFamilies", "ecs:DescribeTaskDefinition"
],
"Resource": "*"
},
{
"Sid": "LambdaReadOnly",
"Effect": "Allow",
"Action": [
"lambda:ListFunctions", "lambda:GetAccountSettings", "lambda:GetFunctionConcurrency",
"lambda:GetFunctionEventInvokeConfig", "lambda:ListTags"
],
"Resource": "*"
},
{
"Sid": "SecretsManagerReadOnly",
"Effect": "Allow",
"Action": ["secretsmanager:ListSecrets", "secretsmanager:DescribeSecret"],
"Resource": "*"
},
{
"Sid": "KMSReadOnly",
"Effect": "Allow",
"Action": [
"kms:ListKeys", "kms:DescribeKey", "kms:GetKeyRotationStatus", "kms:GetKeyPolicy"
],
"Resource": "*"
},
{
"Sid": "GuardDutyReadOnly",
"Effect": "Allow",
"Action": ["guardduty:ListDetectors", "guardduty:GetDetector", "guardduty:ListFindings"],
"Resource": "*"
},
{
"Sid": "SecurityHubReadOnly",
"Effect": "Allow",
"Action": ["securityhub:DescribeHub", "securityhub:ListStandardsSubscriptions"],
"Resource": "*"
},
{
"Sid": "ConfigReadOnly",
"Effect": "Allow",
"Action": [
"config:DescribeConfigurationRecorders", "config:DescribeConfigurationRecorderStatus",
"config:DescribeConfigRules"
],
"Resource": "*"
},
{
"Sid": "InspectorReadOnly",
"Effect": "Allow",
"Action": ["inspector2:BatchGetAccountStatus"],
"Resource": "*"
},
{
"Sid": "MacieReadOnly",
"Effect": "Allow",
"Action": ["macie2:GetMacieSession"],
"Resource": "*"
},
{
"Sid": "AccessAnalyzerReadOnly",
"Effect": "Allow",
"Action": ["access-analyzer:ListAnalyzers"],
"Resource": "*"
},
{
"Sid": "CloudTrailReadOnly",
"Effect": "Allow",
"Action": [
"cloudtrail:DescribeTrails", "cloudtrail:GetTrailStatus", "cloudtrail:GetEventSelectors"
],
"Resource": "*"
},
{
"Sid": "CloudFrontReadOnly",
"Effect": "Allow",
"Action": ["cloudfront:ListDistributions", "cloudfront:GetDistribution"],
"Resource": "*"
},
{
"Sid": "ACMReadOnly",
"Effect": "Allow",
"Action": ["acm:ListCertificates", "acm:DescribeCertificate"],
"Resource": "*"
},
{
"Sid": "ELBReadOnly",
"Effect": "Allow",
"Action": [
"elasticloadbalancing:DescribeLoadBalancers", "elasticloadbalancing:DescribeLoadBalancerAttributes",
"elasticloadbalancing:DescribeTargetGroups", "elasticloadbalancing:DescribeTargetGroupAttributes",
"elasticloadbalancing:DescribeTargetHealth"
],
"Resource": "*"
},
{
"Sid": "AutoScalingReadOnly",
"Effect": "Allow",
"Action": [
"autoscaling:DescribeAutoScalingGroups", "autoscaling:DescribePolicies",
"autoscaling:DescribeScheduledActions"
],
"Resource": "*"
},
{
"Sid": "ApplicationAutoScalingReadOnly",
"Effect": "Allow",
"Action": [
"application-autoscaling:DescribeScalableTargets",
"application-autoscaling:DescribeScalingPolicies"
],
"Resource": "*"
},
{
"Sid": "ElastiCacheReadOnly",
"Effect": "Allow",
"Action": ["elasticache:DescribeReplicationGroups"],
"Resource": "*"
},
{
"Sid": "DynamoDBReadOnly",
"Effect": "Allow",
"Action": [
"dynamodb:ListTables", "dynamodb:DescribeTable", "dynamodb:DescribeContinuousBackups"
],
"Resource": "*"
},
{
"Sid": "EFSReadOnly",
"Effect": "Allow",
"Action": ["elasticfilesystem:DescribeFileSystems", "elasticfilesystem:DescribeBackupPolicy"],
"Resource": "*"
},
{
"Sid": "SQSReadOnly",
"Effect": "Allow",
"Action": ["sqs:ListQueues", "sqs:GetQueueAttributes"],
"Resource": "*"
},
{
"Sid": "Route53ReadOnly",
"Effect": "Allow",
"Action": [
"route53:ListHealthChecks", "route53:ListHostedZones", "route53:ListResourceRecordSets"
],
"Resource": "*"
},
{
"Sid": "BackupReadOnly",
"Effect": "Allow",
"Action": ["backup:ListBackupPlans"],
"Resource": "*"
},
{
"Sid": "ServiceQuotasReadOnly",
"Effect": "Allow",
"Action": ["servicequotas:GetServiceQuota"],
"Resource": "*"
},
{
"Sid": "ECRReadOnly",
"Effect": "Allow",
"Action": ["ecr:DescribeRepositories", "ecr:ListImages", "ecr:GetLifecyclePolicy"],
"Resource": "*"
},
{
"Sid": "CloudWatchReadOnly",
"Effect": "Allow",
"Action": ["cloudwatch:DescribeAlarms", "cloudwatch:ListDashboards"],
"Resource": "*"
},
{
"Sid": "CloudWatchLogsReadOnly",
"Effect": "Allow",
"Action": ["logs:DescribeLogGroups"],
"Resource": "*"
},
{
"Sid": "SSMReadOnly",
"Effect": "Allow",
"Action": [
"ssm:DescribeInstanceInformation", "ssm:DescribeMaintenanceWindows",
"ssm:ListAssociations", "ssm:DescribeParameters"
],
"Resource": "*"
},
{
"Sid": "SNSReadOnly",
"Effect": "Allow",
"Action": ["sns:ListTopics", "sns:GetTopicAttributes"],
"Resource": "*"
},
{
"Sid": "EventBridgeReadOnly",
"Effect": "Allow",
"Action": ["events:ListRules"],
"Resource": "*"
},
{
"Sid": "CloudFormationReadOnly",
"Effect": "Allow",
"Action": ["cloudformation:DescribeStacks"],
"Resource": "*"
},
{
"Sid": "SyntheticsReadOnly",
"Effect": "Allow",
"Action": ["synthetics:DescribeCanaries"],
"Resource": "*"
},
{
"Sid": "CostExplorerReadOnly",
"Effect": "Allow",
"Action": ["ce:ListCostAllocationTags"],
"Resource": "*"
},
{
"Sid": "SavingsPlansReadOnly",
"Effect": "Allow",
"Action": ["savingsplans:DescribeSavingsPlans"],
"Resource": "*"
}
]
}
Checks that lack permission are reported as Scanner Coverage Gaps in the PDF — they never fail silently.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file aws_war_lens-0.1.7.tar.gz.
File metadata
- Download URL: aws_war_lens-0.1.7.tar.gz
- Upload date:
- Size: 174.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
17fe0a2bbc56a29928676ba34e6cedaefe51e16086bd7ff6517ef83fb9191500
|
|
| MD5 |
01527e3672594abcaa7d7a25cd4ad21b
|
|
| BLAKE2b-256 |
363d78831588f20aea396597951cb62f0859178377a8074707ac42ae157609bc
|
File details
Details for the file aws_war_lens-0.1.7-py3-none-any.whl.
File metadata
- Download URL: aws_war_lens-0.1.7-py3-none-any.whl
- Upload date:
- Size: 119.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
902951488eb3c6bbf442bd2f95bbae60394ea95cdea3b667f73f9b40baf0f291
|
|
| MD5 |
98904ff8f578722f3eb58f6a08e8a689
|
|
| BLAKE2b-256 |
1a2e4130772b7a77e7a8163b9aec0308c0efeff191812b0d9b261ca02c52aa35
|