Metadata-Version: 2.4
Name: aws-war-lens
Version: 0.1.5
Summary: AWS Well-Architected Review — automated assessment and PDF report generator
Requires-Python: >=3.13
Requires-Dist: anthropic>=0.102.0
Requires-Dist: boto3>=1.34.96
Requires-Dist: jinja2>=3.1.6
Requires-Dist: python-dotenv>=1.2.1
Requires-Dist: weasyprint>=66.0
Description-Content-Type: text/markdown

# AWS WAR Lens

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

```bash
pip install aws-war-lens
```

> **Note:** WeasyPrint (used for PDF generation) requires native system libraries on Linux/macOS. See [WeasyPrint installation docs](https://doc.courtbouillon.org/weasyprint/stable/first_steps.html) if you hit dependency errors.

## Usage

```bash
# 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.

```bash
export ANTHROPIC_API_KEY=sk-ant-...
aws-war-lens --region eu-west-1
```

## Docker

```bash
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:**
```bash
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.

---

<details>
<summary>Copy IAM policy JSON</summary>

```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": "*"
    }
  ]
}
```

</details>

Checks that lack permission are reported as **Scanner Coverage Gaps** in the PDF — they never fail silently.
