Initial Scrivas AWS discovery deliverables
Read-only cloud discovery of the Scrivas AWS Organization (o-qfj0pvhhv7) to inform a proposal. - scripts/: boto3 org assessment, member-account assessment, fast discovery - findings/: self-contained HTML dashboards, written report, summary JSON, and the rendered Prowler benchmark report - docs/full_discovery_plan.md: phased full-discovery plan - index.html: landing page linking all reports - Pipfile/.python-version: reproducible pipenv env (Python 3.12.11) Large raw scans (OCSF JSON, CSV, compliance/) are git-ignored.
This commit is contained in:
+24
@@ -0,0 +1,24 @@
|
|||||||
|
# --- Python environment (rebuild from Pipfile.lock) ---
|
||||||
|
.venv/
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
# note: .python-version IS committed (pins pyenv interpreter for the repo)
|
||||||
|
|
||||||
|
# --- Large raw scan artifacts (keep rendered HTML + summary JSON) ---
|
||||||
|
findings/prowler/*.ocsf.json
|
||||||
|
findings/prowler/*.csv
|
||||||
|
findings/prowler/*.json.ocsf
|
||||||
|
findings/prowler/prowler_run.log
|
||||||
|
findings/prowler/compliance/
|
||||||
|
output/
|
||||||
|
|
||||||
|
# --- Local AWS / secrets (never commit) ---
|
||||||
|
.aws/
|
||||||
|
*.pem
|
||||||
|
*.env
|
||||||
|
.env
|
||||||
|
|
||||||
|
# --- OS / editor cruft ---
|
||||||
|
.DS_Store
|
||||||
|
*.swp
|
||||||
|
.idea/
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3.12.11
|
||||||
Vendored
+8
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python",
|
||||||
|
"python.terminal.activateEnvironment": true,
|
||||||
|
"python.venvPath": "${workspaceFolder}",
|
||||||
|
"files.exclude": {
|
||||||
|
"**/.venv": false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
[[source]]
|
||||||
|
url = "https://pypi.org/simple"
|
||||||
|
verify_ssl = true
|
||||||
|
name = "pypi"
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
boto3 = "*"
|
||||||
|
prowler = "*"
|
||||||
|
|
||||||
|
[dev-packages]
|
||||||
|
|
||||||
|
[requires]
|
||||||
|
python_version = "3.12"
|
||||||
|
|
||||||
|
[scripts]
|
||||||
|
org-assessment = "python scripts/org_assessment.py"
|
||||||
|
member-assessment = "python scripts/assess_member_account.py"
|
||||||
|
fast-discovery = "python scripts/fast_discovery.py"
|
||||||
Generated
+3957
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,104 @@
|
|||||||
|
# Scrivas — Full AWS Discovery Plan
|
||||||
|
|
||||||
|
**Owner:** Dasnuve · **Prepared:** 2026-08-19 · **Status:** proposed
|
||||||
|
**Access:** `dasnuve-scrivas-louis-impersonation` → `iam/louis` (mgmt acct `716468089330`, groups `Admin`/`devops`), cross-account `OrganizationAccountAccessRole` into members.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
We have completed a preliminary Organizations assessment (see `findings/discovery_report.md`). It confirmed a two-account org (`o-qfj0pvhhv7`: *Scrivas Admin* + *Lazka*), workloads running in the management account (EKS/EC2/RDS), an active compliance stack (Intruder, Secureframe, Macie, Config), and one headline gap — **no delegated administrators**. This document defines the **full discovery** needed to size and shape the proposal: the complete cloud footprint, its cost, its security posture, and its operational maturity across every account and region.
|
||||||
|
|
||||||
|
**Goal:** a defensible, evidence-backed picture of Scrivas's AWS estate that lets us (a) scope remediation/landing-zone work and (b) quantify value (cost, risk, effort).
|
||||||
|
|
||||||
|
**Principles:** read-only only; scripted + reproducible (everything lands in `scripts/` + `findings/`); per-account and per-region coverage; no changes to client resources; secrets/PII never exfiltrated, only referenced.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 0 — Access, scoping & guardrails *(½ day)*
|
||||||
|
- Confirm the role/permissions boundary of `iam/louis`; document what we can and cannot read (`iam get-account-authorization-details`, simulate key actions).
|
||||||
|
- Enumerate every account (`organizations list-accounts`) and confirm an assumable path into each (`OrganizationAccountAccessRole` / Control Tower exec role). Record any account we **cannot** reach — that gap is itself a finding.
|
||||||
|
- Enumerate enabled regions per account (`account list-regions` / `ec2 describe-regions`) to bound the scan surface.
|
||||||
|
- **Deliverable:** `findings/access_matrix.json` (account × reachable × role × regions).
|
||||||
|
|
||||||
|
## Phase 1 — Organization & governance *(mostly done)*
|
||||||
|
- Org structure, OUs, SCP inventory **and policy contents** (`organizations list-policies`, `describe-policy`, `list-targets-for-policy`).
|
||||||
|
- Delegated admins, trusted access, org resource policy, IAM role trust — ✅ done (`scripts/org_assessment.py`).
|
||||||
|
- Account-level metadata: alternate contacts, root MFA/usage, account status (`account get-alternate-contact`).
|
||||||
|
- **Deliverable:** extend `org_assessment_report.json` with SCP bodies + OU tree.
|
||||||
|
|
||||||
|
## Phase 2 — Identity & access management *(1 day)* — *per account*
|
||||||
|
- Users, groups, roles, policies (managed + inline); password policy; access-key age & last-used.
|
||||||
|
- MFA coverage (console users without MFA), root account usage, unused credentials.
|
||||||
|
- IAM Access Analyzer external-access findings; SSO / Identity Center config if present.
|
||||||
|
- **Deliverable:** `findings/iam_<account>.json` + a consolidated IAM posture summary.
|
||||||
|
|
||||||
|
## Phase 3 — Resource footprint inventory *(2 days)* — *per account × region*
|
||||||
|
Primary engine: **Resource Explorer** / **Config aggregator** / **`tag:GetResources`** for a fast cross-service index, then targeted service calls for depth:
|
||||||
|
|
||||||
|
| Domain | What we enumerate |
|
||||||
|
|---|---|
|
||||||
|
| Compute | EC2 (instances, AMIs, EBS), Lambda, ECS, **EKS** (clusters, node groups, versions), Batch, Auto Scaling |
|
||||||
|
| Storage | S3 (buckets, public-access config, encryption, size via CloudWatch), EBS, EFS, FSx, Backup |
|
||||||
|
| Database | RDS/Aurora, DynamoDB, ElastiCache, Redshift, OpenSearch |
|
||||||
|
| Networking | VPCs, subnets, route tables, IGW/NAT, TGW, peering, VPN/DX, ELB/ALB/NLB, Route 53, CloudFront, WAF |
|
||||||
|
| Serverless/Integration | API Gateway, SQS, SNS, EventBridge, Step Functions |
|
||||||
|
| Containers/Registry | ECR repos & image scan status |
|
||||||
|
| Edge/DNS | Route 53 zones, ACM certs (expiry), CloudFront distributions |
|
||||||
|
|
||||||
|
- **Deliverable:** `findings/inventory_<account>.json` + a rolled-up resource census (counts by service/region) feeding the dashboard.
|
||||||
|
|
||||||
|
## Phase 4 — Security posture *(1–2 days)*
|
||||||
|
- **Detective controls:** GuardDuty findings, Security Hub standards + failed controls, Inspector findings, Macie sensitive-data results, Access Analyzer.
|
||||||
|
- **Preventive/config:** AWS Config rule compliance, Config recorder coverage per account/region.
|
||||||
|
- **Exposure:** public S3, public EBS snapshots/AMIs, security groups open to `0.0.0.0/0`, public RDS/ELB, public IPs.
|
||||||
|
- **Logging & audit:** CloudTrail org-trail status & coverage, log-file validation, VPC Flow Logs coverage, CloudWatch log retention.
|
||||||
|
- **Data protection:** KMS key inventory & rotation, encryption-at-rest coverage, Secrets Manager/SSM Parameter usage.
|
||||||
|
- **Deliverable:** `findings/security_posture.json` + prioritized risk register (severity × exploitability × blast radius).
|
||||||
|
|
||||||
|
## Phase 5 — Cost & optimization *(1 day)*
|
||||||
|
- Cost Explorer: 12-month trend, spend by account/service/region, month-over-month; anomalies.
|
||||||
|
- Commitment coverage: Savings Plans / Reserved Instances utilization & coverage.
|
||||||
|
- Waste signals: idle/unattached EBS, unassociated EIPs, idle NAT gateways, oversized instances, old snapshots, Graviton/spot opportunities, gp2→gp3.
|
||||||
|
- Budgets & Cost Anomaly Detection presence.
|
||||||
|
- **Deliverable:** `findings/cost_baseline.json` + a quantified savings estimate for the proposal.
|
||||||
|
|
||||||
|
## Phase 6 — Operational & resilience maturity *(1 day)*
|
||||||
|
- Backup coverage (AWS Backup plans, RDS/EBS snapshot policies), cross-region/DR posture.
|
||||||
|
- IaC footprint: CloudFormation stacks, Terraform state hints (S3 backends), drift signals.
|
||||||
|
- Tagging hygiene & coverage (via tag editor / Config) — needed for cost allocation & governance.
|
||||||
|
- Service quotas nearing limits; Trusted Advisor checks (needs Business/Enterprise support).
|
||||||
|
- Patch/SSM managed-instance coverage; EKS/RDS end-of-life versions.
|
||||||
|
- **Deliverable:** `findings/ops_maturity.json`.
|
||||||
|
|
||||||
|
## Phase 7 — Synthesis & deliverables *(1–2 days)*
|
||||||
|
- Consolidate all `findings/*.json` into a **discovery data model**.
|
||||||
|
- Update `findings/assessment_dashboard.html` into a full multi-section dashboard (footprint census, cost trend, risk register, maturity scorecard).
|
||||||
|
- Written **Discovery Report** + **Proposal input pack**: prioritized roadmap (landing zone / delegated-admin security account / cost optimization / OU + SCP guardrails), effort estimates, and quantified value.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Tooling approach
|
||||||
|
- **Primary:** scripted boto3 (extend the existing `scripts/` pattern — session per account via assume-role, paginated read-only calls, JSON to `findings/`).
|
||||||
|
- **Accelerators (read-only, optional):** Steampipe/`aws` mods, Prowler (security), or the AWS "Account Assessment for AWS Organizations" solution for cross-account resource-policy scanning. Note any that require deploying resources — prefer client sign-off first.
|
||||||
|
- Everything reproducible from the repo; no console-only steps in the critical path.
|
||||||
|
|
||||||
|
## Timeline & effort
|
||||||
|
| Phase | Effort |
|
||||||
|
|---|---|
|
||||||
|
| 0 Access & scoping | 0.5 d |
|
||||||
|
| 1 Governance | done + 0.5 d |
|
||||||
|
| 2 IAM | 1 d |
|
||||||
|
| 3 Resource inventory | 2 d |
|
||||||
|
| 4 Security posture | 1.5 d |
|
||||||
|
| 5 Cost | 1 d |
|
||||||
|
| 6 Ops/resilience | 1 d |
|
||||||
|
| 7 Synthesis & deliverables | 1.5 d |
|
||||||
|
| **Total** | **~9 working days** |
|
||||||
|
|
||||||
|
## Assumptions & risks
|
||||||
|
- `iam/louis` retains `Admin`-level read access and `OrganizationAccountAccessRole` remains assumable into members.
|
||||||
|
- Some data needs paid tiers (Trusted Advisor full checks = Business/Enterprise support; Compute Optimizer must be opted in).
|
||||||
|
- New accounts/regions added mid-engagement expand scope.
|
||||||
|
- Strictly read-only; any assessment tooling that provisions resources requires explicit client approval.
|
||||||
|
|
||||||
|
## Out of scope (unless requested)
|
||||||
|
Application-level code review, penetration testing, non-AWS cloud, and any write/remediation actions.
|
||||||
@@ -0,0 +1,259 @@
|
|||||||
|
<title>Scrivas — AWS Organizations Assessment</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #eef0f3;
|
||||||
|
--panel: #ffffff;
|
||||||
|
--panel-2: #f7f8fa;
|
||||||
|
--ink: #171d27;
|
||||||
|
--ink-soft: #4a5563;
|
||||||
|
--ink-faint: #79828f;
|
||||||
|
--line: #dde1e7;
|
||||||
|
--line-soft: #e8ebef;
|
||||||
|
--accent: #0d6e78; /* considered teal — technical, not AWS-orange */
|
||||||
|
--accent-ink:#0a565e;
|
||||||
|
--good: #2f8657;
|
||||||
|
--warn: #b5832a;
|
||||||
|
--crit: #bd463b;
|
||||||
|
--crit-bg: #fbeceb;
|
||||||
|
--good-bg: #eaf4ee;
|
||||||
|
--warn-bg: #f8f1e2;
|
||||||
|
--mono: ui-monospace, "SF Mono", "SFMono-Regular", Menlo, Consolas, monospace;
|
||||||
|
--sans: ui-sans-serif, system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
--shadow: 0 1px 2px rgba(20,26,38,.04), 0 6px 20px -12px rgba(20,26,38,.18);
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--bg:#0d1117; --panel:#161c26; --panel-2:#1b222e; --ink:#e8ecf1;
|
||||||
|
--ink-soft:#aab3c0; --ink-faint:#7c8695; --line:#28303c; --line-soft:#212934;
|
||||||
|
--accent:#3bb4bf; --accent-ink:#8fdfe6;
|
||||||
|
--good:#54b681; --warn:#d6a94a; --crit:#e26b60;
|
||||||
|
--crit-bg:#2a1917; --good-bg:#132419; --warn-bg:#251d10;
|
||||||
|
--shadow: 0 1px 2px rgba(0,0,0,.3), 0 8px 24px -14px rgba(0,0,0,.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:root[data-theme="dark"] {
|
||||||
|
--bg:#0d1117; --panel:#161c26; --panel-2:#1b222e; --ink:#e8ecf1;
|
||||||
|
--ink-soft:#aab3c0; --ink-faint:#7c8695; --line:#28303c; --line-soft:#212934;
|
||||||
|
--accent:#3bb4bf; --accent-ink:#8fdfe6;
|
||||||
|
--good:#54b681; --warn:#d6a94a; --crit:#e26b60;
|
||||||
|
--crit-bg:#2a1917; --good-bg:#132419; --warn-bg:#251d10;
|
||||||
|
--shadow: 0 1px 2px rgba(0,0,0,.3), 0 8px 24px -14px rgba(0,0,0,.6);
|
||||||
|
}
|
||||||
|
:root[data-theme="light"] {
|
||||||
|
--bg:#eef0f3; --panel:#ffffff; --panel-2:#f7f8fa; --ink:#171d27;
|
||||||
|
--ink-soft:#4a5563; --ink-faint:#79828f; --line:#dde1e7; --line-soft:#e8ebef;
|
||||||
|
--accent:#0d6e78; --accent-ink:#0a565e; --good:#2f8657; --warn:#b5832a; --crit:#bd463b;
|
||||||
|
--crit-bg:#fbeceb; --good-bg:#eaf4ee; --warn-bg:#f8f1e2;
|
||||||
|
--shadow: 0 1px 2px rgba(20,26,38,.04), 0 6px 20px -12px rgba(20,26,38,.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
margin:0; background:var(--bg); color:var(--ink);
|
||||||
|
font-family:var(--sans); line-height:1.5;
|
||||||
|
-webkit-font-smoothing:antialiased;
|
||||||
|
}
|
||||||
|
.wrap { max-width:1080px; margin:0 auto; padding:32px 24px 64px; }
|
||||||
|
|
||||||
|
/* Header */
|
||||||
|
header { display:flex; flex-wrap:wrap; align-items:flex-end; justify-content:space-between; gap:16px; padding-bottom:20px; border-bottom:2px solid var(--ink); }
|
||||||
|
.eyebrow { font-size:11px; letter-spacing:.16em; text-transform:uppercase; color:var(--accent); font-weight:700; }
|
||||||
|
h1 { font-size:clamp(24px,4vw,34px); margin:6px 0 4px; letter-spacing:-.02em; text-wrap:balance; font-weight:750; }
|
||||||
|
.sub { color:var(--ink-soft); font-size:14px; }
|
||||||
|
.idcard { font-family:var(--mono); font-size:12px; color:var(--ink-soft); text-align:right; line-height:1.7; }
|
||||||
|
.idcard b { color:var(--ink); }
|
||||||
|
|
||||||
|
/* KPI row */
|
||||||
|
.kpis { display:grid; grid-template-columns:repeat(4,1fr); gap:14px; margin:22px 0 30px; }
|
||||||
|
@media (max-width:720px){ .kpis{ grid-template-columns:repeat(2,1fr);} }
|
||||||
|
.kpi { background:var(--panel); border:1px solid var(--line); border-radius:12px; padding:16px 16px 14px; box-shadow:var(--shadow); position:relative; overflow:hidden; }
|
||||||
|
.kpi::before { content:""; position:absolute; left:0; top:0; bottom:0; width:3px; background:var(--accent); }
|
||||||
|
.kpi.crit::before { background:var(--crit); }
|
||||||
|
.kpi.good::before { background:var(--good); }
|
||||||
|
.kpi .n { font-size:30px; font-weight:750; letter-spacing:-.02em; font-variant-numeric:tabular-nums; }
|
||||||
|
.kpi.crit .n { color:var(--crit); }
|
||||||
|
.kpi .lbl { font-size:12px; color:var(--ink-faint); margin-top:2px; }
|
||||||
|
.kpi .meta { font-size:11px; color:var(--ink-soft); margin-top:8px; }
|
||||||
|
|
||||||
|
/* Panels */
|
||||||
|
section { background:var(--panel); border:1px solid var(--line); border-radius:14px; padding:22px 24px; margin-bottom:18px; box-shadow:var(--shadow); }
|
||||||
|
.sec-head { display:flex; align-items:baseline; gap:10px; margin-bottom:16px; }
|
||||||
|
.sec-head .tag { font-family:var(--mono); font-size:12px; color:var(--accent); font-weight:700; }
|
||||||
|
.sec-head h2 { font-size:17px; margin:0; letter-spacing:-.01em; }
|
||||||
|
.sec-head .note { margin-left:auto; font-size:12px; color:var(--ink-faint); }
|
||||||
|
|
||||||
|
.chips { display:flex; flex-wrap:wrap; gap:8px; }
|
||||||
|
.chip { font-family:var(--mono); font-size:12px; padding:5px 10px; border-radius:999px; background:var(--panel-2); border:1px solid var(--line); color:var(--ink-soft); }
|
||||||
|
.chip.on { border-color:var(--accent); color:var(--accent-ink); background:color-mix(in srgb, var(--accent) 8%, var(--panel)); }
|
||||||
|
|
||||||
|
.pill { display:inline-flex; align-items:center; gap:6px; font-size:11px; font-weight:700; padding:3px 9px; border-radius:999px; letter-spacing:.02em; }
|
||||||
|
.pill.crit { background:var(--crit-bg); color:var(--crit); }
|
||||||
|
.pill.good { background:var(--good-bg); color:var(--good); }
|
||||||
|
.pill.warn { background:var(--warn-bg); color:var(--warn); }
|
||||||
|
.dot { width:7px; height:7px; border-radius:50%; background:currentColor; }
|
||||||
|
|
||||||
|
.callout { display:flex; gap:14px; align-items:flex-start; background:var(--crit-bg); border:1px solid color-mix(in srgb, var(--crit) 35%, transparent); border-radius:10px; padding:14px 16px; }
|
||||||
|
.callout .big { font-size:26px; font-weight:750; color:var(--crit); line-height:1; font-variant-numeric:tabular-nums; }
|
||||||
|
.callout p { margin:0; font-size:13.5px; color:var(--ink-soft); }
|
||||||
|
.callout b { color:var(--ink); }
|
||||||
|
|
||||||
|
/* trust rows */
|
||||||
|
.trust { display:flex; flex-direction:column; gap:10px; }
|
||||||
|
.trow { display:flex; flex-wrap:wrap; align-items:center; gap:10px; padding:12px 14px; border:1px solid var(--line); border-radius:10px; background:var(--panel-2); }
|
||||||
|
.trow .rn { font-family:var(--mono); font-size:13px; font-weight:600; }
|
||||||
|
.trow .arw { color:var(--ink-faint); }
|
||||||
|
.trow .acct { font-family:var(--mono); font-size:12.5px; color:var(--ink-soft); }
|
||||||
|
.trow .vend { margin-left:auto; font-size:12px; color:var(--ink-soft); }
|
||||||
|
|
||||||
|
/* findings table */
|
||||||
|
.tbl-wrap { overflow-x:auto; }
|
||||||
|
table { width:100%; border-collapse:collapse; font-size:13.5px; min-width:520px; }
|
||||||
|
th { text-align:left; font-size:11px; letter-spacing:.08em; text-transform:uppercase; color:var(--ink-faint); font-weight:700; padding:0 12px 10px; border-bottom:1px solid var(--line); }
|
||||||
|
td { padding:13px 12px; border-bottom:1px solid var(--line-soft); vertical-align:top; color:var(--ink-soft); }
|
||||||
|
td b { color:var(--ink); font-weight:600; }
|
||||||
|
tr:last-child td { border-bottom:none; }
|
||||||
|
.sev { white-space:nowrap; }
|
||||||
|
|
||||||
|
footer { margin-top:24px; font-size:12px; color:var(--ink-faint); display:flex; flex-wrap:wrap; gap:6px 16px; }
|
||||||
|
footer code { font-family:var(--mono); color:var(--ink-soft); }
|
||||||
|
a { color:var(--accent-ink); }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="wrap">
|
||||||
|
<header>
|
||||||
|
<div>
|
||||||
|
<div class="eyebrow">Dasnuve · Cloud Discovery</div>
|
||||||
|
<h1>Scrivas — AWS Organizations Assessment</h1>
|
||||||
|
<div class="sub">Delegated administration, trust policies & trusted access · read-only enumeration</div>
|
||||||
|
</div>
|
||||||
|
<div class="idcard">
|
||||||
|
org <b>o-qfj0pvhhv7</b><br>
|
||||||
|
mgmt <b>716468089330</b><br>
|
||||||
|
as <b>iam/louis</b> · 2026-08-19
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="kpis">
|
||||||
|
<div class="kpi good">
|
||||||
|
<div class="n">2</div><div class="lbl">Accounts in org</div>
|
||||||
|
<div class="meta">Scrivas Admin · Lazka — no OUs (flat)</div>
|
||||||
|
</div>
|
||||||
|
<div class="kpi good">
|
||||||
|
<div class="n">6</div><div class="lbl">Trusted access services</div>
|
||||||
|
<div class="meta">CloudTrail, GuardDuty, SecurityHub, Inspector…</div>
|
||||||
|
</div>
|
||||||
|
<div class="kpi crit">
|
||||||
|
<div class="n">0</div><div class="lbl">Delegated administrators</div>
|
||||||
|
<div class="meta">Security ops run from the mgmt account</div>
|
||||||
|
</div>
|
||||||
|
<div class="kpi">
|
||||||
|
<div class="n">2</div><div class="lbl">Third-party cross-account trusts</div>
|
||||||
|
<div class="meta">Intruder.io · Secureframe (both ExternalId-gated)</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div class="sec-head"><span class="tag">01</span><h2>Trusted access services</h2>
|
||||||
|
<span class="note">org-enabled service principals</span></div>
|
||||||
|
<div class="chips">
|
||||||
|
<span class="chip on">cloudtrail</span>
|
||||||
|
<span class="chip on">securityhub</span>
|
||||||
|
<span class="chip on">guardduty</span>
|
||||||
|
<span class="chip on">malware-protection.guardduty</span>
|
||||||
|
<span class="chip on">inspector2</span>
|
||||||
|
<span class="chip on">notifications</span>
|
||||||
|
</div>
|
||||||
|
<p class="sub" style="margin:14px 0 0">Org-wide security tooling has trusted access enabled — Scrivas has begun
|
||||||
|
centralizing security. <span class="pill good"><span class="dot"></span>on track</span></p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div class="sec-head"><span class="tag">02</span><h2>Delegated administrators</h2>
|
||||||
|
<span class="note">list_delegated_administrators</span></div>
|
||||||
|
<div class="callout">
|
||||||
|
<div class="big">0</div>
|
||||||
|
<p><b>No delegated administrator is set</b> despite trusted access being enabled for GuardDuty,
|
||||||
|
Security Hub and Inspector. These services are therefore administered directly from the
|
||||||
|
<b>management account</b>. Best practice is to delegate them to a dedicated security/audit
|
||||||
|
account and keep the management account minimal.
|
||||||
|
<span class="pill crit" style="margin-top:8px"><span class="dot"></span>headline gap</span></p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div class="sec-head"><span class="tag">03</span><h2>Trust policies</h2>
|
||||||
|
<span class="note">org resource policy + IAM role trust relationships</span></div>
|
||||||
|
<p class="sub" style="margin:0 0 14px">Organization resource policy: <b>none set</b>. Of 44 IAM roles in the
|
||||||
|
management account, all but three are AWS service / service-linked roles. Notable external & federated trusts:</p>
|
||||||
|
<div class="trust">
|
||||||
|
<div class="trow">
|
||||||
|
<span class="rn">IntruderReadOnlyRole</span><span class="arw">→</span>
|
||||||
|
<span class="acct">123311413059:root</span>
|
||||||
|
<span class="pill good"><span class="dot"></span>ExternalId</span>
|
||||||
|
<span class="vend">Intruder.io — vulnerability scanning</span>
|
||||||
|
</div>
|
||||||
|
<div class="trow">
|
||||||
|
<span class="rn">SecureframeRole-f983f1e89008</span><span class="arw">→</span>
|
||||||
|
<span class="acct">728997465891:root</span>
|
||||||
|
<span class="pill good"><span class="dot"></span>ExternalId</span>
|
||||||
|
<span class="vend">Secureframe — SOC 2 / compliance</span>
|
||||||
|
</div>
|
||||||
|
<div class="trow">
|
||||||
|
<span class="rn">AmazonEKS_EBS_CSI_DriverRole</span><span class="arw">→</span>
|
||||||
|
<span class="acct">oidc.eks.us-east-2</span>
|
||||||
|
<span class="pill warn"><span class="dot"></span>federated</span>
|
||||||
|
<span class="vend">EKS OIDC (IRSA)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class="sub" style="margin:14px 0 0"><b>Lazka</b> member account (6 roles): only default service-linked roles +
|
||||||
|
<span class="chip" style="padding:2px 8px">OrganizationAccountAccessRole</span> — <b>no</b> GuardDuty/SecurityHub/Inspector
|
||||||
|
roles, so org security services are not deployed into the member account.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div class="sec-head"><span class="tag">04</span><h2>Governance signals for the proposal</h2></div>
|
||||||
|
<div class="tbl-wrap">
|
||||||
|
<table>
|
||||||
|
<thead><tr><th>Severity</th><th>Observation</th><th>Why it matters</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="sev"><span class="pill crit"><span class="dot"></span>High</span></td>
|
||||||
|
<td><b>No delegated administrators</b> despite trusted access</td>
|
||||||
|
<td>Security ops run from the mgmt account; violates multi-account best practice</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="sev"><span class="pill crit"><span class="dot"></span>High</span></td>
|
||||||
|
<td><b>Workloads in the management account</b> — EKS, EC2, RDS, VPC flow logs</td>
|
||||||
|
<td>Blast-radius & separation-of-duties risk; mgmt account should be minimal</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="sev"><span class="pill warn"><span class="dot"></span>Medium</span></td>
|
||||||
|
<td><b>Member account not monitored</b> — Lazka lacks security SLRs</td>
|
||||||
|
<td>Coverage gap; trusted access enabled but not delivered to members</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="sev"><span class="pill warn"><span class="dot"></span>Medium</span></td>
|
||||||
|
<td><b>Flat org, no OUs</b> — SCPs enabled but unused for targeting</td>
|
||||||
|
<td>No policy boundaries; governance won't scale as accounts are added</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="sev"><span class="pill good"><span class="dot"></span>Signal</span></td>
|
||||||
|
<td>Compliance stack present — Intruder, Secureframe, Macie, Config, Access Analyzer</td>
|
||||||
|
<td>Client is actively pursuing compliance — receptive to a landing-zone engagement</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<span>Read-only · boto3 via <code>dasnuve-scrivas-louis-impersonation</code></span>
|
||||||
|
<span>Reproduce: <code>scripts/org_assessment.py</code> · <code>scripts/assess_member_account.py</code></span>
|
||||||
|
<span>Raw: <code>findings/*.json</code></span>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// keep tabular figures aligned; no external deps
|
||||||
|
document.documentElement.setAttribute('data-render','ready');
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,303 @@
|
|||||||
|
<title>Scrivas — AWS Discovery Findings</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg:#eef0f3; --panel:#ffffff; --panel-2:#f7f8fa; --ink:#171d27;
|
||||||
|
--ink-soft:#4a5563; --ink-faint:#79828f; --line:#dde1e7; --line-soft:#e8ebef;
|
||||||
|
--accent:#0d6e78; --accent-ink:#0a565e;
|
||||||
|
--good:#2f8657; --warn:#b5832a; --crit:#bd463b;
|
||||||
|
--crit-bg:#fbeceb; --good-bg:#eaf4ee; --warn-bg:#f8f1e2;
|
||||||
|
--mono: ui-monospace,"SF Mono","SFMono-Regular",Menlo,Consolas,monospace;
|
||||||
|
--sans: ui-sans-serif,system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
||||||
|
--shadow:0 1px 2px rgba(20,26,38,.04),0 6px 20px -12px rgba(20,26,38,.18);
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme:dark){:root{
|
||||||
|
--bg:#0d1117;--panel:#161c26;--panel-2:#1b222e;--ink:#e8ecf1;--ink-soft:#aab3c0;
|
||||||
|
--ink-faint:#7c8695;--line:#28303c;--line-soft:#212934;--accent:#3bb4bf;--accent-ink:#8fdfe6;
|
||||||
|
--good:#54b681;--warn:#d6a94a;--crit:#e26b60;--crit-bg:#2a1917;--good-bg:#132419;--warn-bg:#251d10;
|
||||||
|
--shadow:0 1px 2px rgba(0,0,0,.3),0 8px 24px -14px rgba(0,0,0,.6);}}
|
||||||
|
:root[data-theme="dark"]{
|
||||||
|
--bg:#0d1117;--panel:#161c26;--panel-2:#1b222e;--ink:#e8ecf1;--ink-soft:#aab3c0;
|
||||||
|
--ink-faint:#7c8695;--line:#28303c;--line-soft:#212934;--accent:#3bb4bf;--accent-ink:#8fdfe6;
|
||||||
|
--good:#54b681;--warn:#d6a94a;--crit:#e26b60;--crit-bg:#2a1917;--good-bg:#132419;--warn-bg:#251d10;
|
||||||
|
--shadow:0 1px 2px rgba(0,0,0,.3),0 8px 24px -14px rgba(0,0,0,.6);}
|
||||||
|
:root[data-theme="light"]{
|
||||||
|
--bg:#eef0f3;--panel:#ffffff;--panel-2:#f7f8fa;--ink:#171d27;--ink-soft:#4a5563;
|
||||||
|
--ink-faint:#79828f;--line:#dde1e7;--line-soft:#e8ebef;--accent:#0d6e78;--accent-ink:#0a565e;
|
||||||
|
--good:#2f8657;--warn:#b5832a;--crit:#bd463b;--crit-bg:#fbeceb;--good-bg:#eaf4ee;--warn-bg:#f8f1e2;
|
||||||
|
--shadow:0 1px 2px rgba(20,26,38,.04),0 6px 20px -12px rgba(20,26,38,.18);}
|
||||||
|
|
||||||
|
*{box-sizing:border-box;}
|
||||||
|
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--sans);line-height:1.5;-webkit-font-smoothing:antialiased;}
|
||||||
|
.wrap{max-width:1120px;margin:0 auto;padding:32px 24px 64px;}
|
||||||
|
header{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:16px;padding-bottom:20px;border-bottom:2px solid var(--ink);}
|
||||||
|
.eyebrow{font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--accent);font-weight:700;}
|
||||||
|
h1{font-size:clamp(24px,4vw,34px);margin:6px 0 4px;letter-spacing:-.02em;text-wrap:balance;font-weight:750;}
|
||||||
|
.sub{color:var(--ink-soft);font-size:14px;}
|
||||||
|
.idcard{font-family:var(--mono);font-size:12px;color:var(--ink-soft);text-align:right;line-height:1.7;}
|
||||||
|
.idcard b{color:var(--ink);}
|
||||||
|
|
||||||
|
.kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin:22px 0 12px;}
|
||||||
|
@media (max-width:760px){.kpis{grid-template-columns:repeat(2,1fr);}}
|
||||||
|
.kpi{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:16px;box-shadow:var(--shadow);position:relative;overflow:hidden;}
|
||||||
|
.kpi::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--accent);}
|
||||||
|
.kpi.crit::before{background:var(--crit);}.kpi.warn::before{background:var(--warn);}.kpi.good::before{background:var(--good);}
|
||||||
|
.kpi .n{font-size:28px;font-weight:750;letter-spacing:-.02em;font-variant-numeric:tabular-nums;}
|
||||||
|
.kpi.crit .n{color:var(--crit);}.kpi.warn .n{color:var(--warn);}
|
||||||
|
.kpi .lbl{font-size:12px;color:var(--ink-faint);margin-top:2px;}
|
||||||
|
.kpi .meta{font-size:11px;color:var(--ink-soft);margin-top:8px;}
|
||||||
|
|
||||||
|
section{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:22px 24px;margin-bottom:18px;box-shadow:var(--shadow);}
|
||||||
|
.grid2{display:grid;grid-template-columns:1.15fr .85fr;gap:18px;}
|
||||||
|
@media (max-width:820px){.grid2{grid-template-columns:1fr;}}
|
||||||
|
.sec-head{display:flex;align-items:baseline;gap:10px;margin-bottom:16px;}
|
||||||
|
.sec-head .tag{font-family:var(--mono);font-size:12px;color:var(--accent);font-weight:700;}
|
||||||
|
.sec-head h2{font-size:17px;margin:0;letter-spacing:-.01em;}
|
||||||
|
.sec-head .note{margin-left:auto;font-size:12px;color:var(--ink-faint);}
|
||||||
|
|
||||||
|
/* cost bar chart */
|
||||||
|
.chart{display:flex;align-items:flex-end;gap:10px;height:170px;padding:8px 4px 0;border-bottom:1px solid var(--line);}
|
||||||
|
.bar{flex:1;display:flex;flex-direction:column;justify-content:flex-end;align-items:center;gap:6px;height:100%;}
|
||||||
|
.bar .col{width:100%;max-width:46px;background:color-mix(in srgb,var(--accent) 30%,transparent);border-radius:5px 5px 0 0;transition:height .2s;}
|
||||||
|
.bar.last .col{background:var(--accent);}
|
||||||
|
.bar .v{font-family:var(--mono);font-size:10.5px;color:var(--ink-soft);font-variant-numeric:tabular-nums;}
|
||||||
|
.bar .m{font-size:10.5px;color:var(--ink-faint);}
|
||||||
|
.barscale{display:flex;justify-content:space-between;font-size:11px;color:var(--ink-faint);margin-top:6px;}
|
||||||
|
|
||||||
|
/* horizontal service breakdown */
|
||||||
|
.hb{display:flex;flex-direction:column;gap:9px;}
|
||||||
|
.hb .row{display:grid;grid-template-columns:130px 1fr auto;align-items:center;gap:10px;font-size:12.5px;}
|
||||||
|
.hb .svc{color:var(--ink-soft);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
|
||||||
|
.hb .track{height:9px;background:var(--panel-2);border-radius:5px;overflow:hidden;}
|
||||||
|
.hb .fill{height:100%;background:var(--accent);border-radius:5px;}
|
||||||
|
.hb .amt{font-family:var(--mono);font-variant-numeric:tabular-nums;color:var(--ink);font-size:12px;}
|
||||||
|
|
||||||
|
.stat-row{display:flex;flex-wrap:wrap;gap:22px;}
|
||||||
|
.stat{display:flex;flex-direction:column;}
|
||||||
|
.stat .n{font-size:22px;font-weight:700;font-variant-numeric:tabular-nums;letter-spacing:-.01em;}
|
||||||
|
.stat .l{font-size:11.5px;color:var(--ink-faint);}
|
||||||
|
|
||||||
|
.pill{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:700;padding:3px 9px;border-radius:999px;}
|
||||||
|
.pill.crit{background:var(--crit-bg);color:var(--crit);}.pill.good{background:var(--good-bg);color:var(--good);}.pill.warn{background:var(--warn-bg);color:var(--warn);}
|
||||||
|
.dot{width:7px;height:7px;border-radius:50%;background:currentColor;}
|
||||||
|
|
||||||
|
.tbl-wrap{overflow-x:auto;}
|
||||||
|
table{width:100%;border-collapse:collapse;font-size:13px;min-width:540px;}
|
||||||
|
th{text-align:left;font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-faint);font-weight:700;padding:0 12px 10px;border-bottom:1px solid var(--line);}
|
||||||
|
td{padding:12px;border-bottom:1px solid var(--line-soft);vertical-align:top;color:var(--ink-soft);}
|
||||||
|
td b{color:var(--ink);font-weight:600;}
|
||||||
|
td code{font-family:var(--mono);font-size:12px;}
|
||||||
|
tr:last-child td{border-bottom:none;}
|
||||||
|
.sev{white-space:nowrap;}
|
||||||
|
|
||||||
|
.acctgrid{display:grid;grid-template-columns:1fr 1fr;gap:14px;}
|
||||||
|
@media (max-width:720px){.acctgrid{grid-template-columns:1fr;}}
|
||||||
|
.acct{border:1px solid var(--line);border-radius:11px;padding:16px;background:var(--panel-2);}
|
||||||
|
.acct h3{margin:0 0 4px;font-size:14px;}
|
||||||
|
.acct .aid{font-family:var(--mono);font-size:11.5px;color:var(--ink-faint);}
|
||||||
|
.acct ul{margin:10px 0 0;padding-left:16px;font-size:12.5px;color:var(--ink-soft);line-height:1.7;}
|
||||||
|
|
||||||
|
footer{margin-top:24px;font-size:12px;color:var(--ink-faint);display:flex;flex-wrap:wrap;gap:6px 16px;}
|
||||||
|
footer code{font-family:var(--mono);color:var(--ink-soft);}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="wrap">
|
||||||
|
<header>
|
||||||
|
<div>
|
||||||
|
<div class="eyebrow">Dasnuve · Cloud Discovery · Fast Track</div>
|
||||||
|
<h1>Scrivas — AWS Discovery Findings</h1>
|
||||||
|
<div class="sub">Resource footprint · security posture · cost — phases 2–5, read-only</div>
|
||||||
|
</div>
|
||||||
|
<div class="idcard">org <b>o-qfj0pvhhv7</b><br>2 accounts · us-east-2 primary<br>2026-08-19</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="kpis">
|
||||||
|
<div class="kpi warn"><div class="n">$858<span style="font-size:14px">/mo</span></div><div class="lbl">Current run-rate (Jul)</div><div class="meta">up ~4× from $223 in January</div></div>
|
||||||
|
<div class="kpi crit"><div class="n">4</div><div class="lbl">GuardDuty high/critical findings</div><div class="meta">severity 8–9, both regions, open</div></div>
|
||||||
|
<div class="kpi crit"><div class="n">13</div><div class="lbl">Security groups open to 0.0.0.0/0</div><div class="meta">management account, us-east-2</div></div>
|
||||||
|
<div class="kpi warn"><div class="n">5</div><div class="lbl">Access keys > 90 days old</div><div class="meta">incl. admin keys at 322 & 319 days</div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- COST -->
|
||||||
|
<section>
|
||||||
|
<div class="sec-head"><span class="tag">05</span><h2>Cost baseline</h2><span class="note">Cost Explorer · unblended · payer account</span></div>
|
||||||
|
<div class="grid2">
|
||||||
|
<div>
|
||||||
|
<div class="chart" id="chart">
|
||||||
|
<div class="bar"><div class="col" style="height:26%"></div><div class="v">$223</div><div class="m">Jan</div></div>
|
||||||
|
<div class="bar"><div class="col" style="height:15%"></div><div class="v">$131</div><div class="m">Feb</div></div>
|
||||||
|
<div class="bar"><div class="col" style="height:61%"></div><div class="v">$522</div><div class="m">Mar</div></div>
|
||||||
|
<div class="bar"><div class="col" style="height:85%"></div><div class="v">$732</div><div class="m">Apr</div></div>
|
||||||
|
<div class="bar"><div class="col" style="height:87%"></div><div class="v">$746</div><div class="m">May</div></div>
|
||||||
|
<div class="bar"><div class="col" style="height:96%"></div><div class="v">$821</div><div class="m">Jun</div></div>
|
||||||
|
<div class="bar last"><div class="col" style="height:100%"></div><div class="v">$858</div><div class="m">Jul</div></div>
|
||||||
|
</div>
|
||||||
|
<div class="barscale"><span>Monthly spend, 2026</span><span>+284% Jan→Jul · 7-mo total ≈ $4,033</span></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div style="font-size:12px;color:var(--ink-faint);margin-bottom:10px;">Top services · 6-month spend</div>
|
||||||
|
<div class="hb">
|
||||||
|
<div class="row"><span class="svc">EC2 – Compute</span><span class="track"><span class="fill" style="width:100%"></span></span><span class="amt">$2,722</span></div>
|
||||||
|
<div class="row"><span class="svc">EC2 – Other</span><span class="track"><span class="fill" style="width:14%"></span></span><span class="amt">$375</span></div>
|
||||||
|
<div class="row"><span class="svc">AWS WAF</span><span class="track"><span class="fill" style="width:13%"></span></span><span class="amt">$364</span></div>
|
||||||
|
<div class="row"><span class="svc">Security Hub</span><span class="track"><span class="fill" style="width:6%"></span></span><span class="amt">$153</span></div>
|
||||||
|
<div class="row"><span class="svc">VPC</span><span class="track"><span class="fill" style="width:4%"></span></span><span class="amt">$117</span></div>
|
||||||
|
<div class="row"><span class="svc">Config</span><span class="track"><span class="fill" style="width:3%"></span></span><span class="amt">$84</span></div>
|
||||||
|
<div class="row"><span class="svc">GuardDuty</span><span class="track"><span class="fill" style="width:2%"></span></span><span class="amt">$56</span></div>
|
||||||
|
<div class="row"><span class="svc">EKS</span><span class="track"><span class="fill" style="width:2%"></span></span><span class="amt">$47</span></div>
|
||||||
|
</div>
|
||||||
|
<p class="sub" style="margin:12px 0 0;font-size:12.5px;">EC2 compute is <b>67%</b> of spend — the primary optimization lever (rightsizing, Savings Plans, Graviton/spot). Security tooling (WAF, Security Hub, Config, GuardDuty) adds ~<b>$110/mo</b>.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- FOOTPRINT -->
|
||||||
|
<section>
|
||||||
|
<div class="sec-head"><span class="tag">03</span><h2>Resource footprint</h2><span class="note">active regions only · us-east-2 primary</span></div>
|
||||||
|
<div class="stat-row" style="margin-bottom:16px">
|
||||||
|
<div class="stat"><span class="n">7</span><span class="l">EC2 instances</span></div>
|
||||||
|
<div class="stat"><span class="n">9</span><span class="l">EBS volumes <span style="color:var(--warn)">(2 unattached)</span></span></div>
|
||||||
|
<div class="stat"><span class="n">7</span><span class="l">Elastic IPs</span></div>
|
||||||
|
<div class="stat"><span class="n">9</span><span class="l">S3 buckets</span></div>
|
||||||
|
<div class="stat"><span class="n">2</span><span class="l">Regions in use</span></div>
|
||||||
|
<div class="stat"><span class="n">15</span><span class="l">Idle regions</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="acctgrid">
|
||||||
|
<div class="acct">
|
||||||
|
<h3>Scrivas Admin <span class="pill warn"><span class="dot"></span>management + workloads</span></h3>
|
||||||
|
<div class="aid">716468089330 · us-east-2</div>
|
||||||
|
<ul>
|
||||||
|
<li>7 EC2 · 9 EBS (2 unattached) · 7 EIPs · 9 S3 buckets</li>
|
||||||
|
<li>EKS IAM roles present (staging/gpu/infra/apps) — <b>no live clusters returned</b>; likely torn down, roles orphaned</li>
|
||||||
|
<li>RDS monitoring role present; VPC flow logs enabled</li>
|
||||||
|
<li><b>Anti-pattern:</b> production workloads run in the org management account</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="acct">
|
||||||
|
<h3>Lazka <span class="pill crit"><span class="dot"></span>bare & unmonitored</span></h3>
|
||||||
|
<div class="aid">547868853286 · us-east-1</div>
|
||||||
|
<ul>
|
||||||
|
<li>0 EC2 · 0 S3 · 0 IAM users — effectively empty</li>
|
||||||
|
<li>Only default service-linked roles + OrganizationAccountAccessRole</li>
|
||||||
|
<li>Security Hub <b>not enabled</b>; no GuardDuty detector</li>
|
||||||
|
<li>Covered by the org CloudTrail, but otherwise dark</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- SECURITY + IAM -->
|
||||||
|
<section>
|
||||||
|
<div class="sec-head"><span class="tag">04</span><h2>Security posture & IAM</h2><span class="note">prioritized risk register</span></div>
|
||||||
|
<div class="tbl-wrap">
|
||||||
|
<table>
|
||||||
|
<thead><tr><th>Severity</th><th>Finding</th><th>Evidence</th><th>Why it matters</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="sev"><span class="pill crit"><span class="dot"></span>High</span></td>
|
||||||
|
<td><b>Open GuardDuty high/critical findings</b></td>
|
||||||
|
<td><code>sev 8–9</code> in us-east-1 & us-east-2</td>
|
||||||
|
<td>Active threat signals unremediated; no delegated security owner to triage</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="sev"><span class="pill crit"><span class="dot"></span>High</span></td>
|
||||||
|
<td><b>13 security groups open to the internet</b></td>
|
||||||
|
<td><code>0.0.0.0/0</code> ingress, us-east-2</td>
|
||||||
|
<td>Direct attack surface on the workload account — needs port-level review</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="sev"><span class="pill crit"><span class="dot"></span>High</span></td>
|
||||||
|
<td><b>Stale admin access keys</b></td>
|
||||||
|
<td><code>admin</code> keys 322 & 319 days; +3 others >90d</td>
|
||||||
|
<td>Long-lived static credentials for privileged users — top breach vector</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="sev"><span class="pill warn"><span class="dot"></span>Medium</span></td>
|
||||||
|
<td><b>Console users without MFA</b></td>
|
||||||
|
<td>2 of 10 IAM users</td>
|
||||||
|
<td>Account-takeover risk; fails CIS AWS baseline</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="sev"><span class="pill warn"><span class="dot"></span>Medium</span></td>
|
||||||
|
<td><b>Member account unmonitored</b></td>
|
||||||
|
<td>Lazka: no GuardDuty / Security Hub</td>
|
||||||
|
<td>Detection blind spot; org security services not delivered to members</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="sev"><span class="pill good"><span class="dot"></span>Good</span></td>
|
||||||
|
<td><b>Org CloudTrail configured well</b></td>
|
||||||
|
<td>multi-region + org trail + log-file validation</td>
|
||||||
|
<td>Solid audit foundation to build on</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="sev"><span class="pill good"><span class="dot"></span>Good</span></td>
|
||||||
|
<td><b>No public S3 buckets or public RDS</b></td>
|
||||||
|
<td>0 of 9 buckets public; 0 public DBs</td>
|
||||||
|
<td>Data-exposure basics are in order</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- PROWLER / COMPLIANCE -->
|
||||||
|
<section>
|
||||||
|
<div class="sec-head"><span class="tag">CIS</span><h2>Compliance benchmark</h2><span class="note">Prowler 5.39 · 632 checks · 1,230 findings · mgmt account</span></div>
|
||||||
|
<div class="grid2">
|
||||||
|
<div>
|
||||||
|
<div class="stat-row" style="margin-bottom:14px">
|
||||||
|
<div class="stat"><span class="n" style="color:var(--good)">70%</span><span class="l">Checks passing (861)</span></div>
|
||||||
|
<div class="stat"><span class="n" style="color:var(--crit)">2</span><span class="l">Critical failures</span></div>
|
||||||
|
<div class="stat"><span class="n" style="color:var(--crit)">111</span><span class="l">High failures</span></div>
|
||||||
|
<div class="stat"><span class="n" style="color:var(--warn)">189</span><span class="l">Medium failures</span></div>
|
||||||
|
</div>
|
||||||
|
<div style="font-size:12px;color:var(--ink-faint);margin:4px 0 8px;">Pass rate by framework</div>
|
||||||
|
<div class="hb">
|
||||||
|
<div class="row"><span class="svc">SOC 2</span><span class="track"><span class="fill" style="width:81%;background:var(--good)"></span></span><span class="amt">81%</span></div>
|
||||||
|
<div class="row"><span class="svc">ISO 27001:2022</span><span class="track"><span class="fill" style="width:77%;background:var(--good)"></span></span><span class="amt">77%</span></div>
|
||||||
|
<div class="row"><span class="svc">CIS 3.0</span><span class="track"><span class="fill" style="width:74%;background:var(--warn)"></span></span><span class="amt">74%</span></div>
|
||||||
|
<div class="row"><span class="svc">NIST CSF 2.0</span><span class="track"><span class="fill" style="width:71%;background:var(--warn)"></span></span><span class="amt">71%</span></div>
|
||||||
|
<div class="row"><span class="svc">PCI DSS 4.0</span><span class="track"><span class="fill" style="width:57%;background:var(--crit)"></span></span><span class="amt">57%</span></div>
|
||||||
|
</div>
|
||||||
|
<p class="sub" style="margin:12px 0 0;font-size:12.5px;">SOC 2 at <b>81%</b> aligns with their Secureframe program — a focused push closes the gap. PCI is lowest (not in scope unless they process cards).</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div style="font-size:12px;color:var(--ink-faint);margin-bottom:10px;">Critical & notable high failures</div>
|
||||||
|
<div class="trust">
|
||||||
|
<div class="trow" style="border-color:color-mix(in srgb,var(--crit) 40%,transparent)"><span class="pill crit"><span class="dot"></span>CRIT</span><span class="rn">Root account has no hardware MFA</span></div>
|
||||||
|
<div class="trow" style="border-color:color-mix(in srgb,var(--crit) 40%,transparent)"><span class="pill crit"><span class="dot"></span>CRIT</span><span class="rn">AWS-managed policy grants <code>*:*</code> admin</span></div>
|
||||||
|
<div class="trow"><span class="pill warn"><span class="dot"></span>HIGH</span><span class="rn">No delegated admin: Config / GuardDuty / Security Hub</span></div>
|
||||||
|
<div class="trow"><span class="pill warn"><span class="dot"></span>HIGH</span><span class="rn">IMDSv2 not enforced at account level</span></div>
|
||||||
|
<div class="trow"><span class="pill warn"><span class="dot"></span>HIGH</span><span class="rn">IAM users rely on long-lived credentials</span></div>
|
||||||
|
<div class="trow"><span class="pill warn"><span class="dot"></span>HIGH</span><span class="rn">Secrets Manager rotation disabled (19 secrets)</span></div>
|
||||||
|
<div class="trow"><span class="pill warn"><span class="dot"></span>HIGH</span><span class="rn">KMS key auto-rotation off · SCP region restriction absent</span></div>
|
||||||
|
</div>
|
||||||
|
<p class="sub" style="margin:12px 0 0;font-size:12px;">Full evidence: <code>findings/prowler/*.html</code> · <code>*.ocsf.json</code> · <code>compliance/</code></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ROADMAP -->
|
||||||
|
<section>
|
||||||
|
<div class="sec-head"><span class="tag">→</span><h2>Proposal levers</h2><span class="note">where we create value</span></div>
|
||||||
|
<div class="tbl-wrap">
|
||||||
|
<table>
|
||||||
|
<thead><tr><th>Workstream</th><th>Driver from discovery</th><th>Outcome</th></tr></thead>
|
||||||
|
<tbody>
|
||||||
|
<tr><td><b>Landing zone & account separation</b></td><td>Workloads in mgmt acct; flat org; empty Lazka</td><td>Move workloads to a dedicated account; OUs + SCP guardrails</td></tr>
|
||||||
|
<tr><td><b>Delegated security account</b></td><td>No delegated admin; unaddressed GD findings; Lazka dark</td><td>Central GuardDuty/Security Hub admin; org-wide monitoring</td></tr>
|
||||||
|
<tr><td><b>Cost optimization</b></td><td>EC2 = 67% of spend; 4× growth; unattached EBS</td><td>Rightsizing + Savings Plans + cleanup → recurring savings</td></tr>
|
||||||
|
<tr><td><b>Identity hardening</b></td><td>Stale admin keys; 2 users w/o MFA</td><td>Rotate/retire keys, enforce MFA, move to short-lived roles/SSO</td></tr>
|
||||||
|
<tr><td><b>Exposure reduction</b></td><td>13 internet-open SGs</td><td>Least-privilege ingress; WAF already in place to leverage</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<span>Read-only · boto3 via <code>dasnuve-scrivas-louis-impersonation</code></span>
|
||||||
|
<span>Reproduce: <code>scripts/fast_discovery.py</code></span>
|
||||||
|
<span>Raw: <code>findings/fast_discovery.json</code></span>
|
||||||
|
<span>Benchmark: <code>prowler aws</code> 632 checks · <code>findings/prowler/</code></span>
|
||||||
|
<span>Phases 2–5 + CIS benchmark · phase 6 on ice</span>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# Scrivas — AWS Organizations Discovery & Account Assessment
|
||||||
|
|
||||||
|
**Prepared by:** Dasnuve · **Date:** 2026-08-19
|
||||||
|
**Access used:** `dasnuve-scrivas-louis-impersonation` profile → `arn:aws:iam::716468089330:user/louis` (member of `Admin`, `devops`)
|
||||||
|
**Method:** Read-only boto3 enumeration. Scripts in `scripts/`, raw output in `findings/*.json`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Organization at a glance
|
||||||
|
|
||||||
|
| Property | Value |
|
||||||
|
|---|---|
|
||||||
|
| Organization ID | `o-qfj0pvhhv7` |
|
||||||
|
| Feature set | `ALL` |
|
||||||
|
| Management account | `716468089330` — *Scrivas Admin* (`ScrivasAdmin@scrivas.com`) |
|
||||||
|
| Member accounts | `547868853286` — *Lazka* (`lgarrido@gmail.com`) |
|
||||||
|
| Organizational Units | **None** (flat — all accounts under Root) |
|
||||||
|
| Policy types enabled | `SERVICE_CONTROL_POLICY` |
|
||||||
|
|
||||||
|
Two-account, no-OU structure. All security tooling and all workloads currently live in the **management account**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Assessment findings — the three targets
|
||||||
|
|
||||||
|
### 2a. Trusted access services (6 enabled)
|
||||||
|
`cloudtrail` · `securityhub` · `guardduty` (+ `malware-protection.guardduty`) · `inspector2` · `notifications`
|
||||||
|
|
||||||
|
Org-wide security services have trusted access enabled — the client has started centralizing security.
|
||||||
|
|
||||||
|
### 2b. Delegated administrators — **NONE** ⚠️ *(headline gap)*
|
||||||
|
`list_delegated_administrators` returns empty. GuardDuty, Security Hub, and Inspector have org trusted-access enabled but **no member account is delegated to administer them**, so they are operated directly from the management account. AWS best practice is to delegate these to a dedicated security/audit account and keep the management account free of workloads and day-to-day operations.
|
||||||
|
|
||||||
|
### 2c. Trust policies
|
||||||
|
- **Organization resource policy:** none set.
|
||||||
|
- **IAM role trust relationships (management account, 44 roles):** almost all AWS service / service-linked roles. Two third-party **cross-account** trusts, both correctly gated with an `ExternalId`:
|
||||||
|
- `IntruderReadOnlyRole` → trusts `123311413059` (**Intruder.io** — external vuln scanning)
|
||||||
|
- `SecureframeRole-f983f1e89008` → trusts `728997465891` (**Secureframe** — SOC 2 / compliance automation)
|
||||||
|
- One OIDC-federated EKS role (`AmazonEKS_EBS_CSI_DriverRole`, cluster in `us-east-2`).
|
||||||
|
- **Lazka member account (6 roles):** only default service-linked roles + `OrganizationAccountAccessRole` (trusts the management account). **No GuardDuty/SecurityHub/Inspector roles present** → org-enabled security services are *not* deployed into the member account.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Governance signals for the proposal
|
||||||
|
|
||||||
|
| # | Observation | Why it matters |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | **No delegated administrators** despite trusted access enabled | Security ops run from the mgmt account; violates AWS multi-account best practice |
|
||||||
|
| 2 | **Workloads in the management account** — EKS (`scrivas-staging`, gpu/infra/apps node groups), EC2, RDS, VPC flow logs | Blast-radius & separation-of-duties risk; mgmt account should be minimal |
|
||||||
|
| 3 | **Flat org, no OUs** | No policy boundaries (SCP targeting), hard to scale governance |
|
||||||
|
| 4 | **Member account not monitored** — Lazka lacks security service-linked roles | Coverage gap; trusted access enabled but not delivered to members |
|
||||||
|
| 5 | Third-party access via Intruder + Secureframe; Macie/Config/Access Analyzer present | Client is pursuing compliance — receptive to a landing-zone / governance engagement |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Recommended next discovery steps
|
||||||
|
1. Enumerate the full resource footprint per account (compute/storage/network/cost) to size the proposal.
|
||||||
|
2. Pull SCP contents attached to Root to understand existing guardrails.
|
||||||
|
3. Pull CloudTrail configuration & organization trail status.
|
||||||
|
4. Cost & Usage (Cost Explorer) for spend baseline and optimization signals.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. How to reproduce
|
||||||
|
```bash
|
||||||
|
# Org-level assessment (management account)
|
||||||
|
python3 scripts/org_assessment.py --profile dasnuve-scrivas-louis-impersonation
|
||||||
|
|
||||||
|
# Member-account extension (assumes OrganizationAccountAccessRole)
|
||||||
|
python3 scripts/assess_member_account.py --account 547868853286 --name Lazka
|
||||||
|
```
|
||||||
|
Outputs: `findings/org_assessment_report.json`, `findings/member_lazka_547868853286.json`.
|
||||||
@@ -0,0 +1,461 @@
|
|||||||
|
{
|
||||||
|
"generated": "2026-08-19T16:24:41.068499+00:00",
|
||||||
|
"payer_account": "716468089330",
|
||||||
|
"accounts": [
|
||||||
|
{
|
||||||
|
"account_id": "547868853286",
|
||||||
|
"name": "Lazka",
|
||||||
|
"active_regions": [
|
||||||
|
"us-east-1"
|
||||||
|
],
|
||||||
|
"enabled_region_count": 17,
|
||||||
|
"iam": {
|
||||||
|
"user_count": 0,
|
||||||
|
"users_without_mfa": 0,
|
||||||
|
"stale_keys_over_90d": [],
|
||||||
|
"password_policy": "none set",
|
||||||
|
"account_summary": {
|
||||||
|
"GroupPolicySizeQuota": 5120,
|
||||||
|
"InstanceProfilesQuota": 1000,
|
||||||
|
"Policies": 0,
|
||||||
|
"GroupsPerUserQuota": 10,
|
||||||
|
"InstanceProfiles": 0,
|
||||||
|
"AttachedPoliciesPerUserQuota": 10,
|
||||||
|
"Users": 0,
|
||||||
|
"PoliciesQuota": 1500,
|
||||||
|
"Providers": 0,
|
||||||
|
"AccountMFAEnabled": 0,
|
||||||
|
"AccessKeysPerUserQuota": 2,
|
||||||
|
"AssumeRolePolicySizeQuota": 2048,
|
||||||
|
"PolicyVersionsInUseQuota": 10000,
|
||||||
|
"GlobalEndpointTokenVersion": 1,
|
||||||
|
"VersionsPerPolicyQuota": 5,
|
||||||
|
"AttachedPoliciesPerGroupQuota": 10,
|
||||||
|
"PolicySizeQuota": 6144,
|
||||||
|
"Groups": 0,
|
||||||
|
"AccountSigningCertificatesPresent": 0,
|
||||||
|
"UsersQuota": 5000,
|
||||||
|
"ServerCertificatesQuota": 20,
|
||||||
|
"MFADevices": 0,
|
||||||
|
"UserPolicySizeQuota": 2048,
|
||||||
|
"PolicyVersionsInUse": 6,
|
||||||
|
"ServerCertificates": 0,
|
||||||
|
"Roles": 6,
|
||||||
|
"RolesQuota": 1000,
|
||||||
|
"SigningCertificatesPerUserQuota": 2,
|
||||||
|
"MFADevicesInUse": 0,
|
||||||
|
"RolePolicySizeQuota": 10240,
|
||||||
|
"AttachedPoliciesPerRoleQuota": 20,
|
||||||
|
"AccountAccessKeysPresent": 0,
|
||||||
|
"AccountPasswordPresent": 0,
|
||||||
|
"GroupsQuota": 300
|
||||||
|
},
|
||||||
|
"users": []
|
||||||
|
},
|
||||||
|
"census": {
|
||||||
|
"s3_buckets": 0,
|
||||||
|
"by_region": {
|
||||||
|
"us-east-1": {
|
||||||
|
"(empty)": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exposure": {
|
||||||
|
"public_buckets": [],
|
||||||
|
"open_security_groups": [],
|
||||||
|
"public_rds": []
|
||||||
|
},
|
||||||
|
"security_services": {
|
||||||
|
"cloudtrail": [
|
||||||
|
{
|
||||||
|
"name": "us-east-2",
|
||||||
|
"multi_region": true,
|
||||||
|
"org_trail": true,
|
||||||
|
"log_validation": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"guardduty_findings_by_severity": {},
|
||||||
|
"securityhub": {
|
||||||
|
"us-east-1": "not enabled"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"account_id": "716468089330",
|
||||||
|
"name": "Scrivas Admin",
|
||||||
|
"active_regions": [
|
||||||
|
"us-east-1",
|
||||||
|
"us-east-2"
|
||||||
|
],
|
||||||
|
"enabled_region_count": 17,
|
||||||
|
"iam": {
|
||||||
|
"user_count": 10,
|
||||||
|
"users_without_mfa": 2,
|
||||||
|
"stale_keys_over_90d": [
|
||||||
|
"admin:***H7XN (322d)",
|
||||||
|
"admin:***5KWA (319d)",
|
||||||
|
"igor@devteamspace.com:***6YNU (316d)",
|
||||||
|
"scrivas-storage-user:***AXMK (161d)",
|
||||||
|
"Vasilii:***DFAO (147d)"
|
||||||
|
],
|
||||||
|
"password_policy": "none set",
|
||||||
|
"account_summary": {
|
||||||
|
"GroupPolicySizeQuota": 5120,
|
||||||
|
"InstanceProfilesQuota": 1000,
|
||||||
|
"Policies": 7,
|
||||||
|
"GroupsPerUserQuota": 10,
|
||||||
|
"InstanceProfiles": 3,
|
||||||
|
"AttachedPoliciesPerUserQuota": 10,
|
||||||
|
"Users": 10,
|
||||||
|
"PoliciesQuota": 1500,
|
||||||
|
"Providers": 1,
|
||||||
|
"AccountMFAEnabled": 1,
|
||||||
|
"AccessKeysPerUserQuota": 2,
|
||||||
|
"AssumeRolePolicySizeQuota": 2048,
|
||||||
|
"PolicyVersionsInUseQuota": 10000,
|
||||||
|
"GlobalEndpointTokenVersion": 1,
|
||||||
|
"VersionsPerPolicyQuota": 5,
|
||||||
|
"AttachedPoliciesPerGroupQuota": 10,
|
||||||
|
"PolicySizeQuota": 6144,
|
||||||
|
"Groups": 6,
|
||||||
|
"AccountSigningCertificatesPresent": 0,
|
||||||
|
"UsersQuota": 5000,
|
||||||
|
"ServerCertificatesQuota": 20,
|
||||||
|
"MFADevices": 13,
|
||||||
|
"UserPolicySizeQuota": 2048,
|
||||||
|
"PolicyVersionsInUse": 61,
|
||||||
|
"ServerCertificates": 0,
|
||||||
|
"Roles": 44,
|
||||||
|
"RolesQuota": 1000,
|
||||||
|
"SigningCertificatesPerUserQuota": 2,
|
||||||
|
"MFADevicesInUse": 10,
|
||||||
|
"RolePolicySizeQuota": 10240,
|
||||||
|
"AttachedPoliciesPerRoleQuota": 20,
|
||||||
|
"AccountAccessKeysPresent": 0,
|
||||||
|
"AccountPasswordPresent": 1,
|
||||||
|
"GroupsQuota": 300
|
||||||
|
},
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"user": "admin",
|
||||||
|
"mfa": true,
|
||||||
|
"keys": [
|
||||||
|
{
|
||||||
|
"id": "H7XN",
|
||||||
|
"status": "Active",
|
||||||
|
"age_days": 322,
|
||||||
|
"last_used": "2025-10-29T14:59:00+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5KWA",
|
||||||
|
"status": "Active",
|
||||||
|
"age_days": 319,
|
||||||
|
"last_used": "2025-10-05T09:36:00+00:00"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user": "aksinya",
|
||||||
|
"mfa": true,
|
||||||
|
"keys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user": "Azamat",
|
||||||
|
"mfa": true,
|
||||||
|
"keys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user": "Dilbag",
|
||||||
|
"mfa": true,
|
||||||
|
"keys": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user": "gitlab-ci-ecr-push",
|
||||||
|
"mfa": false,
|
||||||
|
"keys": [
|
||||||
|
{
|
||||||
|
"id": "5Y4W",
|
||||||
|
"status": "Active",
|
||||||
|
"age_days": 62,
|
||||||
|
"last_used": "2026-08-18T17:18:00+00:00"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user": "igor@devteamspace.com",
|
||||||
|
"mfa": true,
|
||||||
|
"keys": [
|
||||||
|
{
|
||||||
|
"id": "6YNU",
|
||||||
|
"status": "Active",
|
||||||
|
"age_days": 316,
|
||||||
|
"last_used": "2025-10-30T19:26:00+00:00"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user": "louis",
|
||||||
|
"mfa": true,
|
||||||
|
"keys": [
|
||||||
|
{
|
||||||
|
"id": "RESB",
|
||||||
|
"status": "Active",
|
||||||
|
"age_days": 1,
|
||||||
|
"last_used": "2026-08-19T16:10:00+00:00"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user": "scrivas-storage-user",
|
||||||
|
"mfa": false,
|
||||||
|
"keys": [
|
||||||
|
{
|
||||||
|
"id": "AXMK",
|
||||||
|
"status": "Active",
|
||||||
|
"age_days": 161,
|
||||||
|
"last_used": "2026-03-18T14:20:00+00:00"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user": "Vasilii",
|
||||||
|
"mfa": true,
|
||||||
|
"keys": [
|
||||||
|
{
|
||||||
|
"id": "DFAO",
|
||||||
|
"status": "Active",
|
||||||
|
"age_days": 147,
|
||||||
|
"last_used": "2026-08-19T15:38:00+00:00"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user": "Yegor",
|
||||||
|
"mfa": true,
|
||||||
|
"keys": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"census": {
|
||||||
|
"s3_buckets": 9,
|
||||||
|
"by_region": {
|
||||||
|
"us-east-1": {
|
||||||
|
"(empty)": true
|
||||||
|
},
|
||||||
|
"us-east-2": {
|
||||||
|
"ec2_instances": 7,
|
||||||
|
"ebs_volumes": 9,
|
||||||
|
"ebs_unattached": 2,
|
||||||
|
"eips": 7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exposure": {
|
||||||
|
"public_buckets": [],
|
||||||
|
"open_security_groups": [
|
||||||
|
{
|
||||||
|
"region": "us-east-2",
|
||||||
|
"group": "sg-0256d81b7afd54cf9",
|
||||||
|
"from_port": 80,
|
||||||
|
"to_port": 80,
|
||||||
|
"proto": "tcp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"region": "us-east-2",
|
||||||
|
"group": "sg-0256d81b7afd54cf9",
|
||||||
|
"from_port": 443,
|
||||||
|
"to_port": 443,
|
||||||
|
"proto": "tcp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"region": "us-east-2",
|
||||||
|
"group": "sg-09c07fdd8e013a9c0",
|
||||||
|
"from_port": 80,
|
||||||
|
"to_port": 80,
|
||||||
|
"proto": "tcp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"region": "us-east-2",
|
||||||
|
"group": "sg-09c07fdd8e013a9c0",
|
||||||
|
"from_port": 443,
|
||||||
|
"to_port": 443,
|
||||||
|
"proto": "tcp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"region": "us-east-2",
|
||||||
|
"group": "sg-09dff2d35b97ded21",
|
||||||
|
"from_port": 80,
|
||||||
|
"to_port": 80,
|
||||||
|
"proto": "tcp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"region": "us-east-2",
|
||||||
|
"group": "sg-09dff2d35b97ded21",
|
||||||
|
"from_port": 443,
|
||||||
|
"to_port": 443,
|
||||||
|
"proto": "tcp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"region": "us-east-2",
|
||||||
|
"group": "sg-00c13b6b4f05b6748",
|
||||||
|
"from_port": 3478,
|
||||||
|
"to_port": 3478,
|
||||||
|
"proto": "udp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"region": "us-east-2",
|
||||||
|
"group": "sg-00c13b6b4f05b6748",
|
||||||
|
"from_port": 80,
|
||||||
|
"to_port": 80,
|
||||||
|
"proto": "tcp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"region": "us-east-2",
|
||||||
|
"group": "sg-00c13b6b4f05b6748",
|
||||||
|
"from_port": 443,
|
||||||
|
"to_port": 443,
|
||||||
|
"proto": "tcp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"region": "us-east-2",
|
||||||
|
"group": "sg-0621cd7244c8006b3",
|
||||||
|
"from_port": 80,
|
||||||
|
"to_port": 80,
|
||||||
|
"proto": "tcp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"region": "us-east-2",
|
||||||
|
"group": "sg-0621cd7244c8006b3",
|
||||||
|
"from_port": 443,
|
||||||
|
"to_port": 443,
|
||||||
|
"proto": "tcp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"region": "us-east-2",
|
||||||
|
"group": "sg-0823502d51c886ab1",
|
||||||
|
"from_port": 80,
|
||||||
|
"to_port": 80,
|
||||||
|
"proto": "tcp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"region": "us-east-2",
|
||||||
|
"group": "sg-0823502d51c886ab1",
|
||||||
|
"from_port": 443,
|
||||||
|
"to_port": 443,
|
||||||
|
"proto": "tcp"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"public_rds": []
|
||||||
|
},
|
||||||
|
"security_services": {
|
||||||
|
"cloudtrail": [
|
||||||
|
{
|
||||||
|
"name": "us-east-2",
|
||||||
|
"multi_region": true,
|
||||||
|
"org_trail": true,
|
||||||
|
"log_validation": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"guardduty_findings_by_severity": {
|
||||||
|
"us-east-1": {
|
||||||
|
"2.0": 4,
|
||||||
|
"5.0": 1,
|
||||||
|
"9.0": 1
|
||||||
|
},
|
||||||
|
"us-east-2": {
|
||||||
|
"8.0": 1,
|
||||||
|
"9.0": 1,
|
||||||
|
"2.0": 3,
|
||||||
|
"5.0": 6
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securityhub": {
|
||||||
|
"us-east-1": "enabled",
|
||||||
|
"us-east-2": "enabled"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cost": {
|
||||||
|
"monthly_total": [
|
||||||
|
{
|
||||||
|
"month": "2026-01-01",
|
||||||
|
"total": 223.29
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"month": "2026-02-01",
|
||||||
|
"total": 130.76
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"month": "2026-03-01",
|
||||||
|
"total": 522.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"month": "2026-04-01",
|
||||||
|
"total": 731.93
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"month": "2026-05-01",
|
||||||
|
"total": 745.58
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"month": "2026-06-01",
|
||||||
|
"total": 821.01
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"month": "2026-07-01",
|
||||||
|
"total": 858.16
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"top_services_6mo": [
|
||||||
|
{
|
||||||
|
"service": "Amazon Elastic Compute Cloud - Compute",
|
||||||
|
"cost": 2722.15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"service": "EC2 - Other",
|
||||||
|
"cost": 374.59
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"service": "AWS WAF",
|
||||||
|
"cost": 364.0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"service": "AWS Security Hub",
|
||||||
|
"cost": 152.97
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"service": "Amazon Virtual Private Cloud",
|
||||||
|
"cost": 116.52
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"service": "AWS Config",
|
||||||
|
"cost": 84.34
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"service": "Amazon GuardDuty",
|
||||||
|
"cost": 56.2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"service": "Amazon Elastic Container Service for Kubernetes",
|
||||||
|
"cost": 46.75
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"service": "AWS CloudTrail",
|
||||||
|
"cost": 41.48
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"service": "AmazonCloudWatch",
|
||||||
|
"cost": 21.42
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"service": "AWS Key Management Service",
|
||||||
|
"cost": 15.18
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"service": "Amazon Elastic Load Balancing",
|
||||||
|
"cost": 10.56
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
{
|
||||||
|
"generated": "2026-08-19T16:10:07.425514+00:00",
|
||||||
|
"account_id": "547868853286",
|
||||||
|
"account_name": "Lazka",
|
||||||
|
"access": {
|
||||||
|
"assumed": true,
|
||||||
|
"role_used": "OrganizationAccountAccessRole",
|
||||||
|
"assumed_arn": "arn:aws:sts::547868853286:assumed-role/OrganizationAccountAccessRole/scrivas-discovery"
|
||||||
|
},
|
||||||
|
"iam_role_trust_policies": [
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForAwsUserNotifications",
|
||||||
|
"Path": "/aws-service-role/notifications.amazonaws.com/",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"AssumeRolePolicyDocument": {
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "notifications.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Action": "sts:AssumeRole"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForCloudTrail",
|
||||||
|
"Path": "/aws-service-role/cloudtrail.amazonaws.com/",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"AssumeRolePolicyDocument": {
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "cloudtrail.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Action": "sts:AssumeRole"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForOrganizations",
|
||||||
|
"Path": "/aws-service-role/organizations.amazonaws.com/",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"AssumeRolePolicyDocument": {
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "organizations.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Action": "sts:AssumeRole"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForSupport",
|
||||||
|
"Path": "/aws-service-role/support.amazonaws.com/",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"AssumeRolePolicyDocument": {
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "support.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Action": "sts:AssumeRole"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForTrustedAdvisor",
|
||||||
|
"Path": "/aws-service-role/trustedadvisor.amazonaws.com/",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"AssumeRolePolicyDocument": {
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "trustedadvisor.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Action": "sts:AssumeRole"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "OrganizationAccountAccessRole",
|
||||||
|
"Path": "/",
|
||||||
|
"Kind": [
|
||||||
|
"cross-account/aws"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"AssumeRolePolicyDocument": {
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Principal": {
|
||||||
|
"AWS": "arn:aws:iam::716468089330:root"
|
||||||
|
},
|
||||||
|
"Action": "sts:AssumeRole"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,923 @@
|
|||||||
|
{
|
||||||
|
"generated": "2026-08-19T16:08:34.074423+00:00",
|
||||||
|
"caller": {
|
||||||
|
"Account": "716468089330",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:user/louis"
|
||||||
|
},
|
||||||
|
"organization": {
|
||||||
|
"Id": "o-qfj0pvhhv7",
|
||||||
|
"Arn": "arn:aws:organizations::716468089330:organization/o-qfj0pvhhv7",
|
||||||
|
"FeatureSet": "ALL",
|
||||||
|
"MasterAccountArn": "arn:aws:organizations::716468089330:account/o-qfj0pvhhv7/716468089330",
|
||||||
|
"MasterAccountId": "716468089330",
|
||||||
|
"MasterAccountEmail": "ScrivasAdmin@scrivas.com",
|
||||||
|
"AvailablePolicyTypes": [
|
||||||
|
{
|
||||||
|
"Type": "SERVICE_CONTROL_POLICY",
|
||||||
|
"Status": "ENABLED"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"trusted_access_services": [
|
||||||
|
{
|
||||||
|
"ServicePrincipal": "cloudtrail.amazonaws.com",
|
||||||
|
"DateEnabled": "2025-10-20T14:05:58.424000-04:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ServicePrincipal": "guardduty.amazonaws.com",
|
||||||
|
"DateEnabled": "2026-02-19T06:26:23.375000-05:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ServicePrincipal": "inspector2.amazonaws.com",
|
||||||
|
"DateEnabled": "2026-02-19T06:26:23.873000-05:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ServicePrincipal": "malware-protection.guardduty.amazonaws.com",
|
||||||
|
"DateEnabled": "2026-02-19T06:26:25.203000-05:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ServicePrincipal": "notifications.amazonaws.com",
|
||||||
|
"DateEnabled": "2026-04-07T11:57:56.557000-04:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ServicePrincipal": "securityhub.amazonaws.com",
|
||||||
|
"DateEnabled": "2026-02-19T06:26:23.087000-05:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"delegated_administrators": [],
|
||||||
|
"org_resource_policy": {
|
||||||
|
"error": "An error occurred (ResourcePolicyNotFoundException) when calling the DescribeResourcePolicy operation: No resource-based policy found."
|
||||||
|
},
|
||||||
|
"iam_role_trust_policies": [
|
||||||
|
{
|
||||||
|
"RoleName": "AmazonEKSAutoClusterRole",
|
||||||
|
"Path": "/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/AmazonEKSAutoClusterRole",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": [
|
||||||
|
"sts:AssumeRole",
|
||||||
|
"sts:TagSession"
|
||||||
|
],
|
||||||
|
"Principal": {
|
||||||
|
"Service": "eks.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AmazonEKSAutoNodeRole",
|
||||||
|
"Path": "/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/AmazonEKSAutoNodeRole",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "ec2.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AmazonEKSPodIdentityAmazonEBSCSIDriverRole",
|
||||||
|
"Path": "/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/AmazonEKSPodIdentityAmazonEBSCSIDriverRole",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": [
|
||||||
|
"sts:AssumeRole",
|
||||||
|
"sts:TagSession"
|
||||||
|
],
|
||||||
|
"Principal": {
|
||||||
|
"Service": "pods.eks.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AmazonEKSPodIdentityAmazonVPCCNIRole",
|
||||||
|
"Path": "/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/AmazonEKSPodIdentityAmazonVPCCNIRole",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": [
|
||||||
|
"sts:AssumeRole",
|
||||||
|
"sts:TagSession"
|
||||||
|
],
|
||||||
|
"Principal": {
|
||||||
|
"Service": "pods.eks.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AmazonEKS_EBS_CSI_DriverRole",
|
||||||
|
"Path": "/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/AmazonEKS_EBS_CSI_DriverRole",
|
||||||
|
"Kind": [
|
||||||
|
"federated"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRoleWithWebIdentity",
|
||||||
|
"Principal": {
|
||||||
|
"Federated": "arn:aws:iam::716468089330:oidc-provider/oidc.eks.us-east-2.amazonaws.com/id/8C10FE346B856864E285348C79AE4EAF"
|
||||||
|
},
|
||||||
|
"Condition": {
|
||||||
|
"StringEquals": {
|
||||||
|
"oidc.eks.us-east-2.amazonaws.com/id/8C10FE346B856864E285348C79AE4EAF:aud": "sts.amazonaws.com",
|
||||||
|
"oidc.eks.us-east-2.amazonaws.com/id/8C10FE346B856864E285348C79AE4EAF:sub": "system:serviceaccount:kube-system:ebs-csi-controller-sa"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "applications-eks-node-group-20251007184911320300000008",
|
||||||
|
"Path": "/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/applications-eks-node-group-20251007184911320300000008",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "ec2.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForAccessAnalyzer",
|
||||||
|
"Path": "/aws-service-role/access-analyzer.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/access-analyzer.amazonaws.com/AWSServiceRoleForAccessAnalyzer",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "access-analyzer.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForAmazonEKS",
|
||||||
|
"Path": "/aws-service-role/eks.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/eks.amazonaws.com/AWSServiceRoleForAmazonEKS",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "eks.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForAmazonEKSNodegroup",
|
||||||
|
"Path": "/aws-service-role/eks-nodegroup.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/eks-nodegroup.amazonaws.com/AWSServiceRoleForAmazonEKSNodegroup",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "eks-nodegroup.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForAmazonGuardDuty",
|
||||||
|
"Path": "/aws-service-role/guardduty.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/guardduty.amazonaws.com/AWSServiceRoleForAmazonGuardDuty",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "guardduty.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForAmazonGuardDutyMalwareProtection",
|
||||||
|
"Path": "/aws-service-role/malware-protection.guardduty.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/malware-protection.guardduty.amazonaws.com/AWSServiceRoleForAmazonGuardDutyMalwareProtection",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "malware-protection.guardduty.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForAmazonInspector2",
|
||||||
|
"Path": "/aws-service-role/inspector2.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/inspector2.amazonaws.com/AWSServiceRoleForAmazonInspector2",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "inspector2.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForAmazonInspector2Agentless",
|
||||||
|
"Path": "/aws-service-role/agentless.inspector2.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/agentless.inspector2.amazonaws.com/AWSServiceRoleForAmazonInspector2Agentless",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "agentless.inspector2.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForAmazonMacie",
|
||||||
|
"Path": "/aws-service-role/macie.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/macie.amazonaws.com/AWSServiceRoleForAmazonMacie",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "macie.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForApplicationInsights",
|
||||||
|
"Path": "/aws-service-role/application-insights.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/application-insights.amazonaws.com/AWSServiceRoleForApplicationInsights",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "application-insights.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForAutoScaling",
|
||||||
|
"Path": "/aws-service-role/autoscaling.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/autoscaling.amazonaws.com/AWSServiceRoleForAutoScaling",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "autoscaling.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForAwsUserNotifications",
|
||||||
|
"Path": "/aws-service-role/notifications.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/notifications.amazonaws.com/AWSServiceRoleForAwsUserNotifications",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "notifications.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForCloudTrail",
|
||||||
|
"Path": "/aws-service-role/cloudtrail.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/cloudtrail.amazonaws.com/AWSServiceRoleForCloudTrail",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "cloudtrail.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForConfig",
|
||||||
|
"Path": "/aws-service-role/config.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/config.amazonaws.com/AWSServiceRoleForConfig",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "config.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForEC2Spot",
|
||||||
|
"Path": "/aws-service-role/spot.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/spot.amazonaws.com/AWSServiceRoleForEC2Spot",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "spot.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForElasticLoadBalancing",
|
||||||
|
"Path": "/aws-service-role/elasticloadbalancing.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/elasticloadbalancing.amazonaws.com/AWSServiceRoleForElasticLoadBalancing",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "elasticloadbalancing.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForGlobalAccelerator",
|
||||||
|
"Path": "/aws-service-role/globalaccelerator.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/globalaccelerator.amazonaws.com/AWSServiceRoleForGlobalAccelerator",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "globalaccelerator.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForOrganizations",
|
||||||
|
"Path": "/aws-service-role/organizations.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/organizations.amazonaws.com/AWSServiceRoleForOrganizations",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "organizations.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForRDS",
|
||||||
|
"Path": "/aws-service-role/rds.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/rds.amazonaws.com/AWSServiceRoleForRDS",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "rds.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForResourceExplorer",
|
||||||
|
"Path": "/aws-service-role/resource-explorer-2.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/resource-explorer-2.amazonaws.com/AWSServiceRoleForResourceExplorer",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "resource-explorer-2.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForSecurityHub",
|
||||||
|
"Path": "/aws-service-role/securityhub.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/securityhub.amazonaws.com/AWSServiceRoleForSecurityHub",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "securityhub.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForSecurityHubV2",
|
||||||
|
"Path": "/aws-service-role/securityhubv2.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/securityhubv2.amazonaws.com/AWSServiceRoleForSecurityHubV2",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "securityhubv2.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForServiceQuotas",
|
||||||
|
"Path": "/aws-service-role/servicequotas.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/servicequotas.amazonaws.com/AWSServiceRoleForServiceQuotas",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "servicequotas.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForSupport",
|
||||||
|
"Path": "/aws-service-role/support.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/support.amazonaws.com/AWSServiceRoleForSupport",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "support.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSServiceRoleForTrustedAdvisor",
|
||||||
|
"Path": "/aws-service-role/trustedadvisor.amazonaws.com/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/aws-service-role/trustedadvisor.amazonaws.com/AWSServiceRoleForTrustedAdvisor",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "trustedadvisor.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "AWSSystemsManagerDefaultEC2InstanceManagementRole",
|
||||||
|
"Path": "/service-role/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/service-role/AWSSystemsManagerDefaultEC2InstanceManagementRole",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "ssm.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "CloudTrailRoleForCloudWatchLogs_MainTrail",
|
||||||
|
"Path": "/service-role/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/service-role/CloudTrailRoleForCloudWatchLogs_MainTrail",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "cloudtrail.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": {
|
||||||
|
"StringEquals": {
|
||||||
|
"aws:SourceArn": "arn:aws:cloudtrail:us-east-2:716468089330:trail/us-east-2",
|
||||||
|
"aws:SourceAccount": "716468089330"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "EC2-CloudWatchAgent-Role",
|
||||||
|
"Path": "/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/EC2-CloudWatchAgent-Role",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "ec2.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "EC2-SSM-Access",
|
||||||
|
"Path": "/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/EC2-SSM-Access",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "ec2.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "EC2SSMRole",
|
||||||
|
"Path": "/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/EC2SSMRole",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "ec2.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "FlowLogsToCloudWatch",
|
||||||
|
"Path": "/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/FlowLogsToCloudWatch",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "vpc-flow-logs.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "gpu-eks-node-group-20251007184911320100000007",
|
||||||
|
"Path": "/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/gpu-eks-node-group-20251007184911320100000007",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "ec2.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "infrastructure-eks-node-group-20251007184911319500000006",
|
||||||
|
"Path": "/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/infrastructure-eks-node-group-20251007184911319500000006",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "ec2.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "IntruderReadOnlyRole",
|
||||||
|
"Path": "/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/IntruderReadOnlyRole",
|
||||||
|
"Kind": [
|
||||||
|
"cross-account/aws"
|
||||||
|
],
|
||||||
|
"Vendor": "Intruder.io (external vulnerability scanning)",
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"AWS": "arn:aws:iam::123311413059:root"
|
||||||
|
},
|
||||||
|
"Condition": {
|
||||||
|
"StringEquals": {
|
||||||
|
"sts:ExternalId": "8bb7691d-579b-41ef-ae48-5cc3983bcc7f"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "rds-monitoring-role",
|
||||||
|
"Path": "/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/rds-monitoring-role",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "monitoring.rds.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "scrivas-staging-cluster-20251007184855668200000001",
|
||||||
|
"Path": "/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/scrivas-staging-cluster-20251007184855668200000001",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "eks.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "SecureframeRole-f983f1e89008",
|
||||||
|
"Path": "/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/SecureframeRole-f983f1e89008",
|
||||||
|
"Kind": [
|
||||||
|
"cross-account/aws"
|
||||||
|
],
|
||||||
|
"Vendor": "Secureframe (SOC 2 / compliance automation)",
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"AWS": "arn:aws:iam::728997465891:root"
|
||||||
|
},
|
||||||
|
"Condition": {
|
||||||
|
"StringEquals": {
|
||||||
|
"sts:ExternalId": "d3f0ba8c-2023-4cf6-8846-f983f1e89008"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "vpc-flow-logs-role",
|
||||||
|
"Path": "/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/vpc-flow-logs-role",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "vpc-flow-logs.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"RoleName": "VPCFlowLogs-Cloudwatch-1781174191538",
|
||||||
|
"Path": "/service-role/",
|
||||||
|
"Arn": "arn:aws:iam::716468089330:role/service-role/VPCFlowLogs-Cloudwatch-1781174191538",
|
||||||
|
"Kind": [
|
||||||
|
"service"
|
||||||
|
],
|
||||||
|
"Vendor": null,
|
||||||
|
"Statements": [
|
||||||
|
{
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": "sts:AssumeRole",
|
||||||
|
"Principal": {
|
||||||
|
"Service": "vpc-flow-logs.amazonaws.com"
|
||||||
|
},
|
||||||
|
"Condition": {
|
||||||
|
"StringEquals": {
|
||||||
|
"aws:SourceAccount": "716468089330"
|
||||||
|
},
|
||||||
|
"ArnLike": {
|
||||||
|
"aws:SourceArn": "arn:aws:ec2:us-east-2:716468089330:vpc-flow-log/*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
+101
@@ -0,0 +1,101 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Scrivas — AWS Discovery Reports</title>
|
||||||
|
<style>
|
||||||
|
:root{
|
||||||
|
--bg:#eef0f3;--panel:#ffffff;--panel-2:#f7f8fa;--ink:#171d27;--ink-soft:#4a5563;
|
||||||
|
--ink-faint:#79828f;--line:#dde1e7;--accent:#0d6e78;--accent-ink:#0a565e;
|
||||||
|
--good:#2f8657;--warn:#b5832a;--crit:#bd463b;--warn-bg:#f8f1e2;
|
||||||
|
--mono:ui-monospace,"SF Mono",Menlo,Consolas,monospace;
|
||||||
|
--sans:ui-sans-serif,system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
||||||
|
--shadow:0 1px 2px rgba(20,26,38,.04),0 6px 20px -12px rgba(20,26,38,.18);
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme:dark){:root{
|
||||||
|
--bg:#0d1117;--panel:#161c26;--panel-2:#1b222e;--ink:#e8ecf1;--ink-soft:#aab3c0;
|
||||||
|
--ink-faint:#7c8695;--line:#28303c;--accent:#3bb4bf;--accent-ink:#8fdfe6;
|
||||||
|
--good:#54b681;--warn:#d6a94a;--crit:#e26b60;--warn-bg:#251d10;
|
||||||
|
--shadow:0 1px 2px rgba(0,0,0,.3),0 8px 24px -14px rgba(0,0,0,.6);}}
|
||||||
|
*{box-sizing:border-box;}
|
||||||
|
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--sans);line-height:1.5;-webkit-font-smoothing:antialiased;}
|
||||||
|
.wrap{max-width:880px;margin:0 auto;padding:44px 24px 64px;}
|
||||||
|
.eyebrow{font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--accent);font-weight:700;}
|
||||||
|
h1{font-size:clamp(26px,4vw,36px);margin:6px 0 6px;letter-spacing:-.02em;font-weight:750;}
|
||||||
|
.lede{color:var(--ink-soft);font-size:15px;max-width:60ch;}
|
||||||
|
.meta{font-family:var(--mono);font-size:12px;color:var(--ink-faint);margin-top:10px;}
|
||||||
|
.cards{display:grid;grid-template-columns:1fr 1fr;gap:16px;margin:30px 0 8px;}
|
||||||
|
@media (max-width:680px){.cards{grid-template-columns:1fr;}}
|
||||||
|
a.card{display:block;text-decoration:none;color:inherit;background:var(--panel);border:1px solid var(--line);
|
||||||
|
border-radius:14px;padding:20px;box-shadow:var(--shadow);position:relative;overflow:hidden;transition:transform .12s,border-color .12s;}
|
||||||
|
a.card:hover{transform:translateY(-2px);border-color:var(--accent);}
|
||||||
|
a.card:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
|
||||||
|
a.card::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--accent);}
|
||||||
|
.card .k{font-family:var(--mono);font-size:11px;color:var(--accent);font-weight:700;letter-spacing:.04em;}
|
||||||
|
.card h2{font-size:16px;margin:8px 0 6px;letter-spacing:-.01em;}
|
||||||
|
.card p{font-size:13px;color:var(--ink-soft);margin:0;}
|
||||||
|
.card .go{font-size:12px;color:var(--accent-ink);font-weight:600;margin-top:12px;display:inline-block;}
|
||||||
|
.full{grid-column:1/-1;}
|
||||||
|
.badge{display:inline-flex;align-items:center;gap:6px;font-size:10.5px;font-weight:700;padding:2px 8px;border-radius:999px;background:var(--warn-bg);color:var(--warn);margin-left:8px;vertical-align:middle;}
|
||||||
|
.files{margin-top:26px;border-top:1px solid var(--line);padding-top:18px;}
|
||||||
|
.files h3{font-size:12px;text-transform:uppercase;letter-spacing:.08em;color:var(--ink-faint);margin:0 0 10px;}
|
||||||
|
.files ul{margin:0;padding:0;list-style:none;display:flex;flex-direction:column;gap:6px;}
|
||||||
|
.files li{font-size:13px;}
|
||||||
|
.files code{font-family:var(--mono);font-size:12px;color:var(--ink-soft);}
|
||||||
|
.files a{color:var(--accent-ink);}
|
||||||
|
.note{margin-top:24px;background:var(--warn-bg);border:1px solid color-mix(in srgb,var(--warn) 30%,transparent);
|
||||||
|
border-radius:10px;padding:13px 16px;font-size:12.5px;color:var(--ink-soft);}
|
||||||
|
.note b{color:var(--ink);}
|
||||||
|
footer{margin-top:28px;font-size:12px;color:var(--ink-faint);font-family:var(--mono);}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="wrap">
|
||||||
|
<div class="eyebrow">Dasnuve · Cloud Discovery</div>
|
||||||
|
<h1>Scrivas — AWS Discovery Reports</h1>
|
||||||
|
<p class="lede">Read-only assessment of the Scrivas AWS Organization (<code>o-qfj0pvhhv7</code>) — footprint,
|
||||||
|
security posture, cost, and Organizations governance. Prepared to inform a proposal.</p>
|
||||||
|
<div class="meta">2 accounts · us-east-2 primary · generated 2026-08-19 · CONFIDENTIAL</div>
|
||||||
|
|
||||||
|
<div class="cards">
|
||||||
|
<a class="card" href="findings/discovery_dashboard.html">
|
||||||
|
<span class="k">FINDINGS</span>
|
||||||
|
<h2>Discovery Findings</h2>
|
||||||
|
<p>Resource footprint, security posture, cost baseline, and the CIS/SOC 2 benchmark summary.</p>
|
||||||
|
<span class="go">Open dashboard →</span>
|
||||||
|
</a>
|
||||||
|
<a class="card" href="findings/assessment_dashboard.html">
|
||||||
|
<span class="k">GOVERNANCE</span>
|
||||||
|
<h2>Organizations Assessment</h2>
|
||||||
|
<p>Delegated administrators, trust policies, and trusted access services across the org.</p>
|
||||||
|
<span class="go">Open dashboard →</span>
|
||||||
|
</a>
|
||||||
|
<a class="card full" href="findings/prowler/prowler-output-716468089330-20260819124039.html">
|
||||||
|
<span class="k">BENCHMARK</span>
|
||||||
|
<h2>Prowler Full Report <span class="badge">needs internet</span></h2>
|
||||||
|
<p>All 632 checks with per-framework compliance (CIS, SOC 2, ISO 27001, NIST, PCI). Interactive tables load styling from a CDN, so open this one online.</p>
|
||||||
|
<span class="go">Open full benchmark →</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="files">
|
||||||
|
<h3>Written report & raw evidence</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="findings/discovery_report.md">discovery_report.md</a> — narrative writeup <code>(renders on GitHub)</code></li>
|
||||||
|
<li><code>findings/org_assessment_report.json</code> — org / trust / delegated-admin raw data</li>
|
||||||
|
<li><code>findings/fast_discovery.json</code> — footprint, security, cost raw data</li>
|
||||||
|
<li><code>findings/member_lazka_547868853286.json</code> — member account raw data</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="note">
|
||||||
|
<b>Confidential — private repo only.</b> These reports contain AWS account IDs, role ARNs, external IDs, and live
|
||||||
|
security findings for a prospective client. The large raw Prowler scans (<code>*.ocsf.json</code>, <code>*.csv</code>)
|
||||||
|
are intentionally git-ignored; regenerate them with <code>pipenv run prowler aws …</code> if needed.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>Dasnuve · read-only discovery · reproduce via scripts/ + Pipfile</footer>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Member-Account Trust Assessment
|
||||||
|
================================
|
||||||
|
Extends the org assessment into a member account by assuming a cross-account
|
||||||
|
role from the management-account profile, then evaluating that account's
|
||||||
|
IAM role trust relationships and any delegated services it is aware of.
|
||||||
|
|
||||||
|
Delegated administrators & trusted-access services are ORG-LEVEL and can only
|
||||||
|
be read from the management account, so this focuses on what is meaningful
|
||||||
|
from inside a member account: IAM role trust policies (service / cross-account
|
||||||
|
/ federated) and account-level org context.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python3 assess_member_account.py --account 547868853286 [--name Lazka]
|
||||||
|
[--role OrganizationAccountAccessRole] [--profile PROFILE]
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from datetime import datetime, date, timezone
|
||||||
|
|
||||||
|
import boto3
|
||||||
|
from botocore.exceptions import ClientError, BotoCoreError
|
||||||
|
|
||||||
|
DEFAULT_PROFILE = "dasnuve-scrivas-louis-impersonation"
|
||||||
|
FINDINGS_DIR = os.path.join(
|
||||||
|
os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "findings")
|
||||||
|
|
||||||
|
VENDOR_ACCOUNTS = {
|
||||||
|
"123311413059": "Intruder.io (external vulnerability scanning)",
|
||||||
|
"728997465891": "Secureframe (SOC 2 / compliance automation)",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _default(o):
|
||||||
|
if isinstance(o, (datetime, date)):
|
||||||
|
return o.isoformat()
|
||||||
|
return str(o)
|
||||||
|
|
||||||
|
|
||||||
|
def paginate(client, op, key, **kwargs):
|
||||||
|
out = []
|
||||||
|
try:
|
||||||
|
for page in client.get_paginator(op).paginate(**kwargs):
|
||||||
|
out.extend(page.get(key, []))
|
||||||
|
except (ClientError, BotoCoreError) as e:
|
||||||
|
return out, str(e)
|
||||||
|
return out, None
|
||||||
|
|
||||||
|
|
||||||
|
def assume(session, account_id, role_name, candidate_roles):
|
||||||
|
"""Try the primary role, then fall back through candidate role names."""
|
||||||
|
sts = session.client("sts")
|
||||||
|
tried = [role_name] + [r for r in candidate_roles if r != role_name]
|
||||||
|
errors = {}
|
||||||
|
for rn in tried:
|
||||||
|
arn = f"arn:aws:iam::{account_id}:role/{rn}"
|
||||||
|
try:
|
||||||
|
creds = sts.assume_role(
|
||||||
|
RoleArn=arn, RoleSessionName="scrivas-discovery")["Credentials"]
|
||||||
|
print(f"[+] Assumed {arn}")
|
||||||
|
return boto3.Session(
|
||||||
|
aws_access_key_id=creds["AccessKeyId"],
|
||||||
|
aws_secret_access_key=creds["SecretAccessKey"],
|
||||||
|
aws_session_token=creds["SessionToken"],
|
||||||
|
), rn, None
|
||||||
|
except (ClientError, BotoCoreError) as e:
|
||||||
|
errors[rn] = str(e)
|
||||||
|
print(f"[-] {rn}: {e}")
|
||||||
|
return None, None, errors
|
||||||
|
|
||||||
|
|
||||||
|
def assess_iam_trust(member_session):
|
||||||
|
iam = member_session.client("iam")
|
||||||
|
roles, err = paginate(iam, "list_roles", "Roles")
|
||||||
|
if err:
|
||||||
|
return {"error": err}
|
||||||
|
trust = []
|
||||||
|
for r in roles:
|
||||||
|
doc = r.get("AssumeRolePolicyDocument", {})
|
||||||
|
flat = json.dumps(doc)
|
||||||
|
kind = []
|
||||||
|
if '"Service"' in flat:
|
||||||
|
kind.append("service")
|
||||||
|
if '"AWS"' in flat:
|
||||||
|
kind.append("cross-account/aws")
|
||||||
|
if '"Federated"' in flat:
|
||||||
|
kind.append("federated")
|
||||||
|
vendor = None
|
||||||
|
for stmt in doc.get("Statement", []):
|
||||||
|
p = stmt.get("Principal", {})
|
||||||
|
aws_p = p.get("AWS") if isinstance(p, dict) else None
|
||||||
|
for acct, name in VENDOR_ACCOUNTS.items():
|
||||||
|
if aws_p and acct in json.dumps(aws_p):
|
||||||
|
vendor = name
|
||||||
|
trust.append({
|
||||||
|
"RoleName": r["RoleName"],
|
||||||
|
"Path": r.get("Path"),
|
||||||
|
"Kind": kind,
|
||||||
|
"Vendor": vendor,
|
||||||
|
"AssumeRolePolicyDocument": doc,
|
||||||
|
})
|
||||||
|
return trust
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("--account", required=True)
|
||||||
|
ap.add_argument("--name", default="member")
|
||||||
|
ap.add_argument("--role", default="OrganizationAccountAccessRole")
|
||||||
|
ap.add_argument("--profile", default=DEFAULT_PROFILE)
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
session = boto3.Session(profile_name=args.profile)
|
||||||
|
|
||||||
|
candidates = ["OrganizationAccountAccessRole", "AWSControlTowerExecution",
|
||||||
|
"OrganizationAccountAccessRole", "AdministratorAccess"]
|
||||||
|
member, role_used, err = assume(session, args.account, args.role, candidates)
|
||||||
|
|
||||||
|
result = {
|
||||||
|
"generated": datetime.now(timezone.utc).isoformat(),
|
||||||
|
"account_id": args.account,
|
||||||
|
"account_name": args.name,
|
||||||
|
}
|
||||||
|
|
||||||
|
if member is None:
|
||||||
|
result["access"] = {"assumed": False, "errors": err}
|
||||||
|
print("\n[!] Could not assume any cross-account role into "
|
||||||
|
f"{args.name} ({args.account}). This itself is a finding: no "
|
||||||
|
"management-account access path exists into the member account.")
|
||||||
|
else:
|
||||||
|
ident = member.client("sts").get_caller_identity()
|
||||||
|
result["access"] = {"assumed": True, "role_used": role_used,
|
||||||
|
"assumed_arn": ident["Arn"]}
|
||||||
|
result["iam_role_trust_policies"] = assess_iam_trust(member)
|
||||||
|
|
||||||
|
roles = result["iam_role_trust_policies"]
|
||||||
|
if isinstance(roles, list):
|
||||||
|
xacct = [r for r in roles if "cross-account/aws" in r["Kind"]]
|
||||||
|
fed = [r for r in roles if "federated" in r["Kind"]]
|
||||||
|
print(f"\n[{args.name} / {args.account}] IAM roles: {len(roles)} | "
|
||||||
|
f"{len(xacct)} cross-account | {len(fed)} federated")
|
||||||
|
for r in xacct:
|
||||||
|
tag = f" [{r['Vendor']}]" if r.get("Vendor") else ""
|
||||||
|
print(f" - {r['RoleName']}{tag}")
|
||||||
|
|
||||||
|
out = os.path.join(FINDINGS_DIR, f"member_{args.name.lower()}_{args.account}.json")
|
||||||
|
os.makedirs(FINDINGS_DIR, exist_ok=True)
|
||||||
|
with open(out, "w") as f:
|
||||||
|
json.dump(result, f, indent=2, default=_default)
|
||||||
|
print(f"\nWritten to: {out}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,294 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Scrivas — Fast-Track Discovery (hours, not days)
|
||||||
|
================================================
|
||||||
|
Read-only. Prioritizes the four highest-leverage phases:
|
||||||
|
- Cost baseline (Cost Explorer, payer account)
|
||||||
|
- Resource census (active regions only)
|
||||||
|
- Security exposure (public S3, open SGs, GuardDuty/SecurityHub/CloudTrail)
|
||||||
|
- IAM quick hits (users, MFA, key age, root usage, password policy)
|
||||||
|
|
||||||
|
Runs across the whole org: management account directly, members via assume-role.
|
||||||
|
Profile is passed into the boto3 Session object.
|
||||||
|
|
||||||
|
Usage: python3 fast_discovery.py [--profile PROFILE]
|
||||||
|
Output: findings/fast_discovery.json
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from datetime import datetime, date, timezone, timedelta
|
||||||
|
|
||||||
|
import boto3
|
||||||
|
from botocore.exceptions import ClientError, BotoCoreError
|
||||||
|
|
||||||
|
DEFAULT_PROFILE = "dasnuve-scrivas-louis-impersonation"
|
||||||
|
FINDINGS = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "findings")
|
||||||
|
MEMBER_ROLE = "OrganizationAccountAccessRole"
|
||||||
|
|
||||||
|
|
||||||
|
def _default(o):
|
||||||
|
return o.isoformat() if isinstance(o, (datetime, date)) else str(o)
|
||||||
|
|
||||||
|
|
||||||
|
def safe(fn, *a, **k):
|
||||||
|
try:
|
||||||
|
return fn(*a, **k), None
|
||||||
|
except (ClientError, BotoCoreError) as e:
|
||||||
|
return None, str(e)
|
||||||
|
|
||||||
|
|
||||||
|
def pg(client, op, key, **k):
|
||||||
|
out = []
|
||||||
|
try:
|
||||||
|
for page in client.get_paginator(op).paginate(**k):
|
||||||
|
out.extend(page.get(key, []))
|
||||||
|
except (ClientError, BotoCoreError) as e:
|
||||||
|
return out, str(e)
|
||||||
|
return out, None
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- regions
|
||||||
|
def active_regions(session):
|
||||||
|
"""Enabled regions that actually contain EC2 instances or EBS volumes,
|
||||||
|
plus us-east-1 (global services). Keeps the scan fast."""
|
||||||
|
ec2 = session.client("ec2", region_name="us-east-1")
|
||||||
|
regs, err = safe(ec2.describe_regions, AllRegions=False)
|
||||||
|
enabled = [r["RegionName"] for r in regs["Regions"]] if regs else ["us-east-1", "us-east-2"]
|
||||||
|
active = set(["us-east-1"])
|
||||||
|
for r in enabled:
|
||||||
|
c = session.client("ec2", region_name=r)
|
||||||
|
insts, _ = safe(c.describe_instances, MaxResults=5)
|
||||||
|
if insts and any(res.get("Instances") for res in insts.get("Reservations", [])):
|
||||||
|
active.add(r); continue
|
||||||
|
vols, _ = safe(c.describe_volumes, MaxResults=5)
|
||||||
|
if vols and vols.get("Volumes"):
|
||||||
|
active.add(r)
|
||||||
|
return sorted(active), enabled
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- IAM
|
||||||
|
def iam_quickhits(session):
|
||||||
|
iam = session.client("iam")
|
||||||
|
out = {}
|
||||||
|
users, err = pg(iam, "list_users", "Users")
|
||||||
|
if err:
|
||||||
|
return {"error": err}
|
||||||
|
detail = []
|
||||||
|
no_mfa = 0
|
||||||
|
stale_keys = []
|
||||||
|
for u in users:
|
||||||
|
name = u["UserName"]
|
||||||
|
mfa, _ = safe(iam.list_mfa_devices, UserName=name)
|
||||||
|
has_mfa = bool(mfa and mfa.get("MFADevices"))
|
||||||
|
if not has_mfa:
|
||||||
|
no_mfa += 1
|
||||||
|
keys, _ = safe(iam.list_access_keys, UserName=name)
|
||||||
|
kinfo = []
|
||||||
|
for k in (keys or {}).get("AccessKeyMetadata", []):
|
||||||
|
age = (datetime.now(timezone.utc) - k["CreateDate"]).days
|
||||||
|
lu, _ = safe(iam.get_access_key_last_used, AccessKeyId=k["AccessKeyId"])
|
||||||
|
last = (lu or {}).get("AccessKeyLastUsed", {}).get("LastUsedDate")
|
||||||
|
kinfo.append({"id": k["AccessKeyId"][-4:], "status": k["Status"],
|
||||||
|
"age_days": age, "last_used": last})
|
||||||
|
if age > 90:
|
||||||
|
stale_keys.append(f"{name}:***{k['AccessKeyId'][-4:]} ({age}d)")
|
||||||
|
detail.append({"user": name, "mfa": has_mfa, "keys": kinfo})
|
||||||
|
pw, _ = safe(iam.get_account_password_policy)
|
||||||
|
summ, _ = safe(iam.get_account_summary)
|
||||||
|
out = {
|
||||||
|
"user_count": len(users),
|
||||||
|
"users_without_mfa": no_mfa,
|
||||||
|
"stale_keys_over_90d": stale_keys,
|
||||||
|
"password_policy": (pw or {}).get("PasswordPolicy", "none set"),
|
||||||
|
"account_summary": (summ or {}).get("SummaryMap", {}),
|
||||||
|
"users": detail,
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- census + exposure
|
||||||
|
def census_and_exposure(session, regions):
|
||||||
|
census = {}
|
||||||
|
exposure = {"public_buckets": [], "open_security_groups": [], "public_rds": []}
|
||||||
|
|
||||||
|
# S3 (global list, per-bucket public-access)
|
||||||
|
s3 = session.client("s3")
|
||||||
|
buckets, err = safe(s3.list_buckets)
|
||||||
|
blist = (buckets or {}).get("Buckets", [])
|
||||||
|
census["s3_buckets"] = len(blist)
|
||||||
|
for b in blist:
|
||||||
|
name = b["Name"]
|
||||||
|
pab, _ = safe(s3.get_public_access_block, Bucket=name)
|
||||||
|
cfg = (pab or {}).get("PublicAccessBlockConfiguration", {})
|
||||||
|
blocked = all([cfg.get("BlockPublicAcls"), cfg.get("IgnorePublicAcls"),
|
||||||
|
cfg.get("BlockPublicPolicy"), cfg.get("RestrictPublicBuckets")])
|
||||||
|
if not blocked:
|
||||||
|
exposure["public_buckets"].append(
|
||||||
|
{"bucket": name, "public_access_block": cfg or "none"})
|
||||||
|
|
||||||
|
# per-region compute/db/net
|
||||||
|
per_region = {}
|
||||||
|
for r in regions:
|
||||||
|
rr = {}
|
||||||
|
ec2 = session.client("ec2", region_name=r)
|
||||||
|
insts, _ = pg(ec2, "describe_instances", "Reservations")
|
||||||
|
rr["ec2_instances"] = sum(len(x.get("Instances", [])) for x in insts)
|
||||||
|
vols, _ = pg(ec2, "describe_volumes", "Volumes")
|
||||||
|
rr["ebs_volumes"] = len(vols)
|
||||||
|
rr["ebs_unattached"] = sum(1 for v in vols if not v.get("Attachments"))
|
||||||
|
eips, _ = safe(ec2.describe_addresses)
|
||||||
|
addrs = (eips or {}).get("Addresses", [])
|
||||||
|
rr["eips"] = len(addrs)
|
||||||
|
rr["eips_unassociated"] = sum(1 for a in addrs if not a.get("AssociationId"))
|
||||||
|
sgs, _ = pg(ec2, "describe_security_groups", "SecurityGroups")
|
||||||
|
for sg in sgs:
|
||||||
|
for perm in sg.get("IpPermissions", []):
|
||||||
|
for ipr in perm.get("IpRanges", []):
|
||||||
|
if ipr.get("CidrIp") == "0.0.0.0/0":
|
||||||
|
exposure["open_security_groups"].append({
|
||||||
|
"region": r, "group": sg["GroupId"],
|
||||||
|
"from_port": perm.get("FromPort"), "to_port": perm.get("ToPort"),
|
||||||
|
"proto": perm.get("IpProtocol")})
|
||||||
|
rds = session.client("rds", region_name=r)
|
||||||
|
dbs, _ = pg(rds, "describe_db_instances", "DBInstances")
|
||||||
|
rr["rds_instances"] = len(dbs)
|
||||||
|
for d in dbs:
|
||||||
|
if d.get("PubliclyAccessible"):
|
||||||
|
exposure["public_rds"].append({"region": r, "db": d["DBInstanceIdentifier"]})
|
||||||
|
lam = session.client("lambda", region_name=r)
|
||||||
|
fns, _ = pg(lam, "list_functions", "Functions")
|
||||||
|
rr["lambda_functions"] = len(fns)
|
||||||
|
eks = session.client("eks", region_name=r)
|
||||||
|
clusters, _ = pg(eks, "list_clusters", "clusters")
|
||||||
|
rr["eks_clusters"] = len(clusters)
|
||||||
|
rr["eks_cluster_names"] = clusters
|
||||||
|
elbv2 = session.client("elbv2", region_name=r)
|
||||||
|
lbs, _ = pg(elbv2, "describe_load_balancers", "LoadBalancers")
|
||||||
|
rr["load_balancers"] = len(lbs)
|
||||||
|
per_region[r] = {k: v for k, v in rr.items() if v not in (0, [], None)} or {"(empty)": True}
|
||||||
|
census["by_region"] = per_region
|
||||||
|
return census, exposure
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- security services
|
||||||
|
def security_services(session, regions):
|
||||||
|
out = {}
|
||||||
|
# CloudTrail (any region lists org+shadow trails)
|
||||||
|
ct = session.client("cloudtrail", region_name="us-east-1")
|
||||||
|
trails, _ = safe(ct.describe_trails, includeShadowTrails=True)
|
||||||
|
tl = (trails or {}).get("trailList", [])
|
||||||
|
out["cloudtrail"] = [{"name": t["Name"], "multi_region": t.get("IsMultiRegionTrail"),
|
||||||
|
"org_trail": t.get("IsOrganizationTrail"),
|
||||||
|
"log_validation": t.get("LogFileValidationEnabled")} for t in tl]
|
||||||
|
# GuardDuty / SecurityHub per active region
|
||||||
|
gd_findings = {}
|
||||||
|
shub = {}
|
||||||
|
for r in regions:
|
||||||
|
g = session.client("guardduty", region_name=r)
|
||||||
|
dets, _ = safe(g.list_detectors)
|
||||||
|
for d in (dets or {}).get("DetectorIds", []):
|
||||||
|
stats, _ = safe(g.get_findings_statistics, DetectorId=d,
|
||||||
|
FindingCriteria={}, FindingStatisticTypes=["COUNT_BY_SEVERITY"])
|
||||||
|
gd_findings[r] = (stats or {}).get("FindingStatistics", {}).get("CountBySeverity", {})
|
||||||
|
s = session.client("securityhub", region_name=r)
|
||||||
|
desc, err = safe(s.describe_hub)
|
||||||
|
shub[r] = "enabled" if desc else "not enabled"
|
||||||
|
out["guardduty_findings_by_severity"] = gd_findings
|
||||||
|
out["securityhub"] = shub
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- cost
|
||||||
|
def cost_baseline(session):
|
||||||
|
ce = session.client("ce", region_name="us-east-1")
|
||||||
|
end = date.today().replace(day=1)
|
||||||
|
start = (end - timedelta(days=185)).replace(day=1)
|
||||||
|
res, err = safe(ce.get_cost_and_usage,
|
||||||
|
TimePeriod={"Start": start.isoformat(), "End": end.isoformat()},
|
||||||
|
Granularity="MONTHLY", Metrics=["UnblendedCost"],
|
||||||
|
GroupBy=[{"Type": "DIMENSION", "Key": "SERVICE"}])
|
||||||
|
if err:
|
||||||
|
return {"error": err}
|
||||||
|
months = []
|
||||||
|
service_totals = {}
|
||||||
|
for period in res["ResultsByTime"]:
|
||||||
|
m = period["TimePeriod"]["Start"]
|
||||||
|
total = 0.0
|
||||||
|
for g in period["Groups"]:
|
||||||
|
amt = float(g["Metrics"]["UnblendedCost"]["Amount"])
|
||||||
|
svc = g["Keys"][0]
|
||||||
|
service_totals[svc] = service_totals.get(svc, 0.0) + amt
|
||||||
|
total += amt
|
||||||
|
months.append({"month": m, "total": round(total, 2)})
|
||||||
|
top = sorted(service_totals.items(), key=lambda x: -x[1])[:12]
|
||||||
|
return {"monthly_total": months,
|
||||||
|
"top_services_6mo": [{"service": s, "cost": round(v, 2)} for s, v in top]}
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------- driver
|
||||||
|
def assess_account(session, account_id, name, is_payer):
|
||||||
|
print(f"\n=== {name} ({account_id}) ===")
|
||||||
|
regions, enabled = active_regions(session)
|
||||||
|
print(f" active regions: {regions} (of {len(enabled)} enabled)")
|
||||||
|
census, exposure = census_and_exposure(session, regions)
|
||||||
|
result = {
|
||||||
|
"account_id": account_id, "name": name,
|
||||||
|
"active_regions": regions, "enabled_region_count": len(enabled),
|
||||||
|
"iam": iam_quickhits(session),
|
||||||
|
"census": census,
|
||||||
|
"exposure": exposure,
|
||||||
|
"security_services": security_services(session, regions),
|
||||||
|
}
|
||||||
|
if is_payer:
|
||||||
|
result["cost"] = cost_baseline(session)
|
||||||
|
print(f" ec2/eks/rds/lambda scanned | public buckets: {len(exposure['public_buckets'])}"
|
||||||
|
f" | open SGs: {len(exposure['open_security_groups'])}"
|
||||||
|
f" | users w/o MFA: {result['iam'].get('users_without_mfa')}")
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("--profile", default=DEFAULT_PROFILE)
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
base = boto3.Session(profile_name=args.profile)
|
||||||
|
payer = base.client("sts").get_caller_identity()["Account"]
|
||||||
|
org = base.client("organizations")
|
||||||
|
accounts, _ = pg(org, "list_accounts", "Accounts")
|
||||||
|
|
||||||
|
report = {"generated": datetime.now(timezone.utc).isoformat(),
|
||||||
|
"payer_account": payer, "accounts": []}
|
||||||
|
|
||||||
|
for a in accounts:
|
||||||
|
if a["Status"] != "ACTIVE":
|
||||||
|
continue
|
||||||
|
aid, name = a["Id"], a["Name"]
|
||||||
|
if aid == payer:
|
||||||
|
sess = base
|
||||||
|
else:
|
||||||
|
sts = base.client("sts")
|
||||||
|
creds, err = safe(sts.assume_role,
|
||||||
|
RoleArn=f"arn:aws:iam::{aid}:role/{MEMBER_ROLE}",
|
||||||
|
RoleSessionName="scrivas-fast-discovery")
|
||||||
|
if err:
|
||||||
|
report["accounts"].append({"account_id": aid, "name": name,
|
||||||
|
"error": f"assume-role failed: {err}"})
|
||||||
|
continue
|
||||||
|
c = creds["Credentials"]
|
||||||
|
sess = boto3.Session(aws_access_key_id=c["AccessKeyId"],
|
||||||
|
aws_secret_access_key=c["SecretAccessKey"],
|
||||||
|
aws_session_token=c["SessionToken"])
|
||||||
|
report["accounts"].append(assess_account(sess, aid, name, aid == payer))
|
||||||
|
|
||||||
|
out = os.path.join(FINDINGS, "fast_discovery.json")
|
||||||
|
os.makedirs(FINDINGS, exist_ok=True)
|
||||||
|
with open(out, "w") as f:
|
||||||
|
json.dump(report, f, indent=2, default=_default)
|
||||||
|
print(f"\nWritten to: {out}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Account Assessment for AWS Organizations
|
||||||
|
=========================================
|
||||||
|
Read-only discovery of an AWS Organization's governance posture.
|
||||||
|
|
||||||
|
Scope:
|
||||||
|
1. Trusted access services (org-enabled service principals)
|
||||||
|
2. Delegated administrators (+ delegated services per admin)
|
||||||
|
3. Trust policies (org resource policy + IAM role trust relationships)
|
||||||
|
|
||||||
|
The AWS profile is passed into the boto3 Session object (never hard-coded creds).
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python3 org_assessment.py [--profile PROFILE] [--out PATH.json]
|
||||||
|
|
||||||
|
Client: Scrivas | Engagement: cloud footprint discovery
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from datetime import datetime, date, timezone
|
||||||
|
|
||||||
|
import boto3
|
||||||
|
from botocore.exceptions import ClientError, BotoCoreError
|
||||||
|
|
||||||
|
DEFAULT_PROFILE = "dasnuve-scrivas-louis-impersonation"
|
||||||
|
DEFAULT_OUT = os.path.join(
|
||||||
|
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||||
|
"findings", "org_assessment_report.json",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Known third-party vendor account IDs, for annotating cross-account trusts.
|
||||||
|
VENDOR_ACCOUNTS = {
|
||||||
|
"123311413059": "Intruder.io (external vulnerability scanning)",
|
||||||
|
"728997465891": "Secureframe (SOC 2 / compliance automation)",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _default(o):
|
||||||
|
if isinstance(o, (datetime, date)):
|
||||||
|
return o.isoformat()
|
||||||
|
return str(o)
|
||||||
|
|
||||||
|
|
||||||
|
def dump(label, obj):
|
||||||
|
print(f"\n{'=' * 70}\n{label}\n{'=' * 70}")
|
||||||
|
print(json.dumps(obj, indent=2, default=_default))
|
||||||
|
|
||||||
|
|
||||||
|
def safe(fn, *args, **kwargs):
|
||||||
|
"""Call an API, returning (data, error_string)."""
|
||||||
|
try:
|
||||||
|
return fn(*args, **kwargs), None
|
||||||
|
except (ClientError, BotoCoreError) as e:
|
||||||
|
return None, str(e)
|
||||||
|
|
||||||
|
|
||||||
|
def paginate(client, op, key, **kwargs):
|
||||||
|
out = []
|
||||||
|
try:
|
||||||
|
for page in client.get_paginator(op).paginate(**kwargs):
|
||||||
|
out.extend(page.get(key, []))
|
||||||
|
except (ClientError, BotoCoreError) as e:
|
||||||
|
return out, str(e)
|
||||||
|
return out, None
|
||||||
|
|
||||||
|
|
||||||
|
def assess(session):
|
||||||
|
ident = session.client("sts").get_caller_identity()
|
||||||
|
org = session.client("organizations")
|
||||||
|
|
||||||
|
report = {
|
||||||
|
"generated": datetime.now(timezone.utc).isoformat(),
|
||||||
|
"caller": {"Account": ident["Account"], "Arn": ident["Arn"]},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Organization overview
|
||||||
|
o, err = safe(org.describe_organization)
|
||||||
|
report["organization"] = o.get("Organization") if o else {"error": err}
|
||||||
|
|
||||||
|
# 1. Trusted access services
|
||||||
|
svcs, err = paginate(org, "list_aws_service_access_for_organization",
|
||||||
|
"EnabledServicePrincipals")
|
||||||
|
report["trusted_access_services"] = {"error": err} if err else svcs
|
||||||
|
|
||||||
|
# 2. Delegated administrators (+ their delegated services)
|
||||||
|
admins, err = paginate(org, "list_delegated_administrators",
|
||||||
|
"DelegatedAdministrators")
|
||||||
|
if err:
|
||||||
|
report["delegated_administrators"] = {"error": err}
|
||||||
|
else:
|
||||||
|
for a in admins:
|
||||||
|
svc, serr = paginate(org, "list_delegated_services_for_account",
|
||||||
|
"DelegatedServices", AccountId=a["Id"])
|
||||||
|
a["DelegatedServices"] = {"error": serr} if serr else svc
|
||||||
|
report["delegated_administrators"] = admins
|
||||||
|
|
||||||
|
# 3a. Trust policies: organization resource-based policy
|
||||||
|
rp, err = safe(org.describe_resource_policy)
|
||||||
|
if err:
|
||||||
|
report["org_resource_policy"] = {"error": err}
|
||||||
|
else:
|
||||||
|
content = rp["ResourcePolicy"]["Content"]
|
||||||
|
report["org_resource_policy"] = json.loads(content) if content else None
|
||||||
|
|
||||||
|
# 3b. Trust policies: IAM role assume-role (trust) policies
|
||||||
|
iam = session.client("iam")
|
||||||
|
roles, err = paginate(iam, "list_roles", "Roles")
|
||||||
|
if err:
|
||||||
|
report["iam_role_trust_policies"] = {"error": err}
|
||||||
|
else:
|
||||||
|
trust = []
|
||||||
|
for r in roles:
|
||||||
|
doc = r.get("AssumeRolePolicyDocument", {})
|
||||||
|
flat = json.dumps(doc)
|
||||||
|
kind = []
|
||||||
|
if '"Service"' in flat:
|
||||||
|
kind.append("service")
|
||||||
|
if '"AWS"' in flat:
|
||||||
|
kind.append("cross-account/aws")
|
||||||
|
if '"Federated"' in flat:
|
||||||
|
kind.append("federated")
|
||||||
|
|
||||||
|
statements = []
|
||||||
|
vendor = None
|
||||||
|
for stmt in doc.get("Statement", []):
|
||||||
|
principal = stmt.get("Principal", {})
|
||||||
|
aws_p = principal.get("AWS") if isinstance(principal, dict) else None
|
||||||
|
for acct, name in VENDOR_ACCOUNTS.items():
|
||||||
|
if aws_p and acct in json.dumps(aws_p):
|
||||||
|
vendor = name
|
||||||
|
statements.append({
|
||||||
|
"Effect": stmt.get("Effect"),
|
||||||
|
"Action": stmt.get("Action"),
|
||||||
|
"Principal": principal,
|
||||||
|
"Condition": stmt.get("Condition"),
|
||||||
|
})
|
||||||
|
trust.append({
|
||||||
|
"RoleName": r["RoleName"],
|
||||||
|
"Path": r.get("Path"),
|
||||||
|
"Arn": r["Arn"],
|
||||||
|
"Kind": kind,
|
||||||
|
"Vendor": vendor,
|
||||||
|
"Statements": statements,
|
||||||
|
})
|
||||||
|
report["iam_role_trust_policies"] = trust
|
||||||
|
|
||||||
|
return report
|
||||||
|
|
||||||
|
|
||||||
|
def summarize(report):
|
||||||
|
"""One-line-per-item human summary of the key governance signals."""
|
||||||
|
print(f"\n{'#' * 70}\n# SUMMARY\n{'#' * 70}")
|
||||||
|
org = report.get("organization", {})
|
||||||
|
print(f"Org: {org.get('Id')} | mgmt acct {org.get('MasterAccountId')} "
|
||||||
|
f"({org.get('MasterAccountEmail')}) | FeatureSet {org.get('FeatureSet')}")
|
||||||
|
|
||||||
|
svcs = report.get("trusted_access_services", [])
|
||||||
|
if isinstance(svcs, list):
|
||||||
|
print(f"Trusted access services: {len(svcs)} -> "
|
||||||
|
+ ", ".join(s["ServicePrincipal"] for s in svcs))
|
||||||
|
|
||||||
|
admins = report.get("delegated_administrators", [])
|
||||||
|
if isinstance(admins, list):
|
||||||
|
flag = " <-- GAP: none set" if not admins else ""
|
||||||
|
print(f"Delegated administrators: {len(admins)}{flag}")
|
||||||
|
|
||||||
|
roles = report.get("iam_role_trust_policies", [])
|
||||||
|
if isinstance(roles, list):
|
||||||
|
xacct = [r for r in roles if "cross-account/aws" in r["Kind"]]
|
||||||
|
fed = [r for r in roles if "federated" in r["Kind"]]
|
||||||
|
print(f"IAM roles: {len(roles)} total | {len(xacct)} cross-account | "
|
||||||
|
f"{len(fed)} federated")
|
||||||
|
for r in xacct:
|
||||||
|
tag = f" [{r['Vendor']}]" if r.get("Vendor") else ""
|
||||||
|
print(f" - {r['RoleName']}{tag}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser(description=__doc__,
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||||
|
ap.add_argument("--profile", default=DEFAULT_PROFILE)
|
||||||
|
ap.add_argument("--out", default=DEFAULT_OUT)
|
||||||
|
ap.add_argument("--quiet", action="store_true",
|
||||||
|
help="skip verbose section dumps, print summary only")
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
session = boto3.Session(profile_name=args.profile)
|
||||||
|
report = assess(session)
|
||||||
|
|
||||||
|
if not args.quiet:
|
||||||
|
dump("CALLER / ORG",
|
||||||
|
{"caller": report["caller"], "organization": report["organization"]})
|
||||||
|
dump("1. TRUSTED ACCESS SERVICES", report["trusted_access_services"])
|
||||||
|
dump("2. DELEGATED ADMINISTRATORS", report["delegated_administrators"])
|
||||||
|
dump("3a. ORG RESOURCE POLICY", report["org_resource_policy"])
|
||||||
|
dump("3b. IAM ROLE TRUST POLICIES", report["iam_role_trust_policies"])
|
||||||
|
|
||||||
|
summarize(report)
|
||||||
|
|
||||||
|
os.makedirs(os.path.dirname(args.out), exist_ok=True)
|
||||||
|
with open(args.out, "w") as f:
|
||||||
|
json.dump(report, f, indent=2, default=_default)
|
||||||
|
print(f"\nFull JSON written to: {args.out}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
Reference in New Issue
Block a user