Skip to main content

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 all scans 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:

  1. --access-key / --secret-key / --session-token flags
  2. --profile named profile from ~/.aws/credentials
  3. AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY environment variables
  4. Default AWS CLI profile
  5. 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

aws_war_lens-0.1.7.tar.gz (174.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

aws_war_lens-0.1.7-py3-none-any.whl (119.3 kB view details)

Uploaded Python 3

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

Hashes for aws_war_lens-0.1.7.tar.gz
Algorithm Hash digest
SHA256 17fe0a2bbc56a29928676ba34e6cedaefe51e16086bd7ff6517ef83fb9191500
MD5 01527e3672594abcaa7d7a25cd4ad21b
BLAKE2b-256 363d78831588f20aea396597951cb62f0859178377a8074707ac42ae157609bc

See more details on using hashes here.

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

Hashes for aws_war_lens-0.1.7-py3-none-any.whl
Algorithm Hash digest
SHA256 902951488eb3c6bbf442bd2f95bbae60394ea95cdea3b667f73f9b40baf0f291
MD5 98904ff8f578722f3eb58f6a08e8a689
BLAKE2b-256 1a2e4130772b7a77e7a8163b9aec0308c0efeff191812b0d9b261ca02c52aa35

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page