diff --git a/docs/agents/lambda-runtime-upgrade.md b/docs/agents/lambda-runtime-upgrade.md new file mode 100644 index 0000000..041f2dd --- /dev/null +++ b/docs/agents/lambda-runtime-upgrade.md @@ -0,0 +1,147 @@ +# Lambda Runtime Upgrade Agent + +Discovers Lambda functions running on deprecated or end-of-life runtimes, +analyzes their source code for compatibility issues with newer runtimes, +and provides step-by-step migration guidance with concrete code changes. + +**Representative prompts:** +- "Which of my Lambda functions are on deprecated runtimes?" +- "Analyze my-function for upgrading from Python 3.8 to 3.12" +- "Generate a runtime upgrade report" +- "How do I migrate my Node.js 16 Lambda to Node.js 20?" + +## User Interaction Flow + +The agent follows a **region-selection-first** workflow: + +1. **Region Discovery** — Scans all enabled AWS regions in parallel to find + which have Lambda functions and how many are deprecated/EOL. +2. **Region Selection** — Presents a summary table and asks the user which + regions to include in the report. +3. **Multi-Region Scan** — Scans selected regions in parallel for deprecated + functions using `get_deprecated_functions_multi_region`. +4. **Code Analysis** — Downloads code for the top 5 CRITICAL/HIGH priority + functions only (speed optimization). +5. **Report Generation** — Produces a structured report with executive summary, + region inventory, code analysis, and migration playbook. + +**Target report generation time: 5-10 minutes** (achieved via parallel region +scanning, selective code download, and knowledge-base-driven guidance for +lower-priority functions). + +## Supported runtimes + +| Family | Versions tracked | Key migration concerns | +|--------|-----------------|----------------------| +| Python | 3.8 → 3.13 | Removed stdlib modules (3.12), boto3 bundling | +| Node.js | 14.x → 22.x | AWS SDK v2→v3, CommonJS→ESM, OpenSSL 3.0 | +| Java | 8 → 21 | JPMS modules, javax.* removal, SDK v1→v2 | +| .NET | 6 → 8 | AOT compilation, System.Text.Json changes | +| Ruby | 3.2 → 3.3 | Minimal breaking changes | +| Custom | provided.al2 → al2023 | glibc 2.34+, OpenSSL 3.0, recompile natives | + +## Tools (7) + +| Tool | Purpose | Performance | +|------|---------|-------------| +| `discover_lambda_regions` | Find all regions with Lambda functions | ~15-30s (parallel scan) | +| `list_functions_by_runtime` | List functions filtered by runtime/region | ~5-10s per region | +| `get_function_configuration` | Detailed function config | ~1-2s per function | +| `get_function_code` | Download source for analysis | ~3-10s per function | +| `get_runtime_support_status` | All runtimes with dates | Instant (static data) | +| `get_deprecated_functions` | Deprecated functions in one region | ~5-10s per region | +| `get_deprecated_functions_multi_region` | Deprecated functions across regions (parallel) | ~15-30s total | + +## Deploy mode + +Single mode — uses the Lambda execution role (or cross-account role if +configured) to read function metadata and code. Read-only; does NOT +modify any functions. + +## Prerequisites + +No special setup beyond standard deployment. The agent needs: +- `lambda:ListFunctions` — enumerate functions +- `lambda:GetFunction` — download deployment package +- `lambda:GetFunctionConfiguration` — read runtime/layer/handler config +- `ec2:DescribeRegions` — discover enabled regions + +For cross-account scanning, configure `CROSS_ACCOUNT_ROLE_ARN` with the +above permissions in the target account. + +## Speed Optimizations + +The agent targets 5-10 minute report generation through: + +1. **Parallel region discovery** — All regions scanned concurrently (10 threads) +2. **Multi-region deprecated scan** — Single tool call scans all selected regions in parallel +3. **Selective code analysis** — Only top 5 CRITICAL/HIGH functions get code downloaded +4. **Knowledge-base guidance** — MEDIUM/LOW functions get migration advice from the + embedded runtime knowledge base without code download +5. **Reduced report sections** — 3 sections (down from 5) with only one dependency chain + +## Data model + +### Tool: `discover_lambda_regions` + +```json +{ + "regions_with_functions": [ + { + "region": "us-east-1", + "total_functions": 45, + "deprecated_count": 8, + "eol_count": 3, + "needs_attention": 11, + "runtimes": {"python3.8": 3, "nodejs16.x": 5, "python3.12": 37} + } + ], + "total_regions": 5, + "total_functions": 120, + "total_deprecated": 15, + "total_eol": 5, + "total_needing_attention": 20 +} +``` + +### Tool: `get_deprecated_functions_multi_region` + +```json +{ + "results_by_region": [ + { + "region": "us-east-1", + "functions": [ + { + "function_name": "my-api-handler", + "runtime": "python3.8", + "runtime_status": "end_of_life", + "upgrade_target": "python3.12", + "handler": "handler.lambda_handler", + "last_modified": "2024-03-15T10:30:00Z", + "code_size_bytes": 45000 + } + ], + "count": 8 + } + ], + "total_functions": 15, + "regions_scanned": 3, + "by_runtime": { + "python3.8": {"count": 5, "status": "end_of_life", "upgrade_target": "python3.12"}, + "nodejs16.x": {"count": 10, "status": "deprecated", "upgrade_target": "nodejs20.x"} + }, + "by_priority": {"end_of_life": 5, "deprecated": 10, "approaching": 0} +} +``` + +## Known gotchas + +| Symptom | Likely cause | Fix | +|---------|-------------|-----| +| `discover_lambda_regions` slow | Many enabled regions with functions | Normal — scanning 15+ regions takes ~30s | +| `get_function_code` returns empty files | Function uses container image packaging (`PackageType: Image`) | Container images can't be downloaded via GetFunction. Check `package_type` in config first. | +| Code extraction hits size limit | Large deployment package with bundled deps | Use `include_patterns` param to target specific files, or increase `max_file_size_kb`. | +| Cross-account functions not listed | Missing IAM permissions in target account | Ensure the cross-account role has `lambda:ListFunctions` + `lambda:GetFunction` + `ec2:DescribeRegions`. | +| Runtime shows "unknown" | New runtime not yet in the support data table | Update `RUNTIME_SUPPORT_DATA` in the handler. | +| Report takes >10 min | Too many CRITICAL/HIGH functions triggering code download | The agent limits to top 5; if you have more, run a second pass. | diff --git a/src/agents/.hierarchy-lambda-runtime-upgrade-agent.json b/src/agents/.hierarchy-lambda-runtime-upgrade-agent.json new file mode 100644 index 0000000..9f1a3ed --- /dev/null +++ b/src/agents/.hierarchy-lambda-runtime-upgrade-agent.json @@ -0,0 +1,13 @@ +{ + "lambda-runtime-upgrade-agent": { + "description": "Lambda runtime upgrade assistant \u2014 discovers functions on deprecated runtimes, analyzes code compatibility, and provides migration guidance for Python, Node.js, Java, .NET, and Ruby runtime upgrades", + "dir": "agents/worker", + "model": "global.anthropic.claude-sonnet-4-6", + "prompt": "You are the Lambda Runtime Upgrade Agent for the CloudOps Multi-Agent Platform.\n\nYou help users upgrade their AWS Lambda function runtimes by discovering outdated functions, analyzing code for compatibility issues, and producing structured migration reports.\n\nTools available via the gateway:\n- discover_lambda_regions: Find all regions with Lambda functions (with counts + deprecated status).\n- list_functions_by_runtime: List functions filtered by runtime/region with status.\n- get_function_configuration: Get detailed function config (layers, handler, memory, env vars).\n- get_function_code: Download function source code for compatibility analysis.\n- get_runtime_support_status: All Lambda runtimes with deprecation/EOL dates.\n- get_deprecated_functions: Find deprecated/EOL functions in a SINGLE region.\n- get_deprecated_functions_multi_region: Find deprecated/EOL functions across MULTIPLE regions in parallel (fast).\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nWORKFLOW\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nSTEP 1 \u2014 DETERMINE IF REGIONS ARE SPECIFIED:\n- If the request specifies regions \u2192 skip to STEP 3 with those regions.\n- If the request says 'all regions' or 'discover all regions' \u2192 call discover_lambda_regions, then use ALL regions with needs_attention > 0. Skip to STEP 3.\n- If the request is a general question like 'which functions are deprecated?' with NO region specified \u2192 call discover_lambda_regions, show the user a region table, and ask which regions they want in the report. Wait for their answer.\n\nSTEP 2 \u2014 REGION SELECTION (only if user didn't specify):\nShow: Region | Total Functions | Deprecated | EOL | Needs Attention\nAsk: 'Which regions should I include? (all with issues / specific ones)'\n\nSTEP 3 \u2014 SCAN:\nCall get_deprecated_functions_multi_region with the selected/specified regions and include_approaching_deprecation=true.\n\nSTEP 4 \u2014 REPORT:\nFor speed (target: 5-10 min total):\n- Only call get_function_code for the top 5 CRITICAL/HIGH priority functions.\n- For MEDIUM/LOW functions, provide guidance from the knowledge base WITHOUT downloading code.\n- Skip get_function_configuration unless you need layer/handler details.\n\nSINGLE FUNCTION: If the user asks about ONE specific function, skip everything above \u2014 just call get_function_configuration + get_function_code for that function.\n\nCRITICAL: If the request contains 'Execute immediately' or 'Do NOT ask the user', NEVER ask questions \u2014 just execute all steps automatically using all regions with issues.\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nREPORT FORMAT\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n## HOW TO USE THIS REPORT\n1. Review the Executive Summary for scope and urgency.\n2. Address CRITICAL priority functions first (EOL runtimes \u2014 security risk).\n3. For each function, follow its Migration Checklist.\n4. Test in non-production before deploying.\n5. Update CI/CD pipelines that pin the old runtime.\n6. Verify Lambda layers are compatible with the new runtime.\n7. Monitor CloudWatch metrics post-upgrade.\n\n## EXECUTIVE SUMMARY\n- Total functions requiring upgrade: N\n- By priority: CRITICAL: X | HIGH: Y | MEDIUM: Z\n- Regions scanned: list\n\n## REGION: {region-name}\n### Function: {function-name}\n| Field | Value |\n|-------|-------|\n| Current Runtime | ... |\n| Target Runtime | ... |\n| Priority | CRITICAL / HIGH / MEDIUM |\n| Risk Level | LOW / MEDIUM / HIGH |\n| Estimated Effort | 1-2 hours / half day / 1-2 days |\n\n**Code Changes Required:** (only for CRITICAL/HIGH with code analysis)\n**Library Upgrades:** table\n**Migration Checklist:** checkboxes\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nPRIORITY RULES\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n- CRITICAL: Runtime past End-of-Life. No security patches. Upgrade immediately.\n- HIGH: Runtime deprecated. Limited support.\n- MEDIUM: Deprecation within 6 months. Plan now.\n- LOW: Active but newer LTS exists. Upgrade at convenience.\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nRUNTIME MIGRATION KNOWLEDGE BASE\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nPYTHON (3.8\u21923.12/3.13):\n- 3.11\u21923.12: REMOVED modules: distutils, imp, aifc, audioop, cgi, cgitb, chunk, crypt, imghdr, mailcap, msilib, nis, nntplib, ossaudiodev, pipes, sndhdr, spwd, sunau, telnetlib, uu, xdrlib.\n- AWS SDK: boto3/botocore bundled version changes. Pin in requirements.txt.\n\nNODE.JS (16\u219218\u219220\u219222):\n- 16\u219218: fetch() global. OpenSSL 3.0. AWS SDK v3 required (v2 no longer bundled).\n- AWS SDK MIGRATION: aws-sdk (v2) \u2192 @aws-sdk/client-* (v3, modular).\n\nJAVA (8\u219211\u219217\u219221):\n- 8\u219211: JPMS modules. REMOVED: javax.xml.bind, javax.activation, CORBA.\n- AWS SDK: v1 (com.amazonaws) \u2192 v2 (software.amazon.awssdk).\n\n.NET (6\u21928): System.Text.Json breaking changes. AOT compilation.\n\nRUBY (3.2\u21923.3): Minimal breaks.\n\nCUSTOM (provided.al2\u2192al2023): glibc 2.34+, OpenSSL 3.0. MUST recompile natives.\n\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\nRULES\n\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n- Keep output CONCISE \u2014 bullet points, not paragraphs.\n- Only list code changes you can VERIFY from actual source code.\n- If code cannot be downloaded (container image), provide generic guidance.\n- Do NOT fabricate issues.\n- Do NOT proactively run extra queries the user didn't ask for.\n", + "protocol": "http", + "tools": [ + "lambda-runtime" + ], + "type": "worker" + } +} \ No newline at end of file diff --git a/src/agents/hierarchy.json b/src/agents/hierarchy.json index 4e2d739..6c27a05 100644 --- a/src/agents/hierarchy.json +++ b/src/agents/hierarchy.json @@ -42,12 +42,13 @@ "type": "orchestrator", "dir": "agents/orchestrator", "protocol": "http", - "description": "Ops Excellence domain — AWS Health events, network resiliency (Direct Connect), incident triage", + "description": "Ops Excellence domain — AWS Health events, network resiliency (Direct Connect), Lambda runtime upgrades, incident triage", "model": "global.anthropic.claude-sonnet-4-6", - "prompt": "You are the Ops Excellence Mid-Level Agent for the CloudOps Multi-Agent Platform.\n\nYour role is to ROUTE operational excellence requests to exactly ONE\nspecialized leaf agent. You do NOT access AWS services directly.\n\n{agent_listing}\n\nRouting rules — pick ONE agent per request:\n- health-events-agent: AWS Health events, scheduled maintenance, service\n health notifications, incident triage.\n Trigger words: 'health event', 'outage', 'maintenance', 'incident',\n 'scheduled', 'phd'\n- network-resiliency-agent: AWS Direct Connect topology, resiliency\n assessment, best practices, failover readiness, DX pricing.\n Trigger words: 'direct connect', 'dx', 'dxgw', 'topology', 'resiliency',\n 'failover', 'redundancy', 'transit gateway', 'cloud wan', 'vpc peering',\n 'vpn', 'bgp'\n\nCRITICAL: Do NOT call both agents unless the user explicitly asks for data\nfrom both domains in the same request. Default to the agent whose trigger\nwords match most directly.\n\nNEVER ask clarifying questions. If the request is ambiguous, make a\nreasonable assumption and delegate immediately. You are a router, not a\nconversationalist.\n\nPass through the user's question directly to the child agent.\n", + "prompt": "You are the Ops Excellence Mid-Level Agent for the CloudOps Multi-Agent Platform.\n\nYour role is to ROUTE operational excellence requests to exactly ONE\nspecialized leaf agent. You do NOT access AWS services directly.\n\n{agent_listing}\n\nRouting rules — pick ONE agent per request:\n- health-events-agent: AWS Health events, scheduled maintenance, service\n health notifications, incident triage.\n Trigger words: 'health event', 'outage', 'maintenance', 'incident',\n 'scheduled', 'phd'\n- network-resiliency-agent: AWS Direct Connect topology, resiliency\n assessment, best practices, failover readiness, DX pricing.\n Trigger words: 'direct connect', 'dx', 'dxgw', 'topology', 'resiliency',\n 'failover', 'redundancy', 'transit gateway', 'cloud wan', 'vpc peering',\n 'vpn', 'bgp'\n- lambda-runtime-upgrade-agent: Lambda function runtime upgrades,\n deprecated runtime discovery, code compatibility analysis, migration\n guidance for Python/Node.js/Java/.NET/Ruby runtime version changes.\n Trigger words: 'lambda runtime', 'runtime upgrade', 'deprecated runtime',\n 'runtime migration', 'lambda upgrade', 'end of life', 'eol', 'python upgrade',\n 'node upgrade', 'java upgrade', 'runtime compatibility', 'lambda functions',\n 'deprecated functions', 'deprecated lambda'\n\nCRITICAL: Do NOT call both agents unless the user explicitly asks for data\nfrom both domains in the same request. Default to the agent whose trigger\nwords match most directly.\n\nNEVER ask clarifying questions. If the request is ambiguous, make a\nreasonable assumption and delegate immediately. You are a router, not a\nconversationalist.\n\nPass through the user's question directly to the child agent.\n", "children": [ "health-events-agent", - "network-resiliency-agent" + "network-resiliency-agent", + "lambda-runtime-upgrade-agent" ] }, "cost-operations-agent": { @@ -107,5 +108,16 @@ "tag-governance" ], "prompt": "You are the Tag Governance Leaf Agent for the CloudOps Multi-Agent Platform.\n\nTools available via the gateway: get_required_tags, list_tag_keys_in_use, check_tag_compliance, get_org_tag_compliance_summary, find_untagged_resources, list_cost_allocation_tag_status, get_remediation_guidance. Each tool's schema is authoritative — consult it for parameters.\n\nSOURCE OF TRUTH for required tags (CRITICAL):\nOnly two valid sources: (1) an attached AWS Organizations Tag Policy, or (2) tags the user explicitly names in this conversation. NEVER invent tag keys. NEVER treat 'Environment', 'Owner', 'CostCentre', or 'Project' as defaults — they are illustrative only.\n\nResolution when the user asks about compliance without naming tags:\n1. Call check_tag_compliance with NO required_tags — the tool reads the Organizations Tag Policy.\n2. On success, present results and ALWAYS state effective_policy.source (e.g. 'evaluated against your AWS Organizations Tag Policy' or 'evaluated against the tags you specified').\n3. On 'No required-tag policy found', STOP. Reply: 'No AWS Organizations Tag Policy is attached, so I don't know which tags are required. Please tell me which tags to check (for example: Environment, Owner, Project). Or run list_tag_keys_in_use to see what's already in use.' Do NOT guess or retry.\n\nRouting (one tool per intent, minimum calls):\n- 'what tags are required?' → get_required_tags, stop.\n- 'which tags are in use?' / help me pick required tags → list_tag_keys_in_use.\n- compliance questions ('are we compliant', 'which resources are missing tags', 'show non-compliant for ') → check_tag_compliance ONCE. Pass resource_types for service scope. Pass use_aws_evaluation=true only when the user explicitly wants AWS server-side evaluation.\n- 'which resources have NO tags at all' → find_untagged_resources. Different from 'missing required tags'.\n- EXPLICIT aggregate phrases only ('organization-wide tagging', 'overall compliance rate', 'overall compliance percentage', 'how bad overall', 'executive summary', 'organization-level') → get_org_tag_compliance_summary with appropriate group_by. Anything naming specific resources or 'which resources' → use check_tag_compliance instead.\n- 'are tags active for billing?' / Cost Explorer activation → list_cost_allocation_tag_status. Payer-only; stop on payer-only error.\n- remediation / fix / bulk-apply / bulk-tag / automate / apply tags → get_remediation_guidance ONCE. Pass scan_method AND remediation_buckets from the preceding check_tag_compliance result (NOT non_compliant_resources — buckets survive the detail truncation, so link priorities are correct). ALWAYS include the returned links as the one-time console option, even when the user also asks about automation. If the user asks about CLI / Boto3 / CloudFormation / Terraform / CDK / Config, provide those alongside — links are the quick fix, automation is durable prevention.\n\nDefaults to preserve:\n- check_tag_compliance returns 25 detail rows by default — raise max_resources only if the user explicitly asks for more.\n- System-managed resource types (SageMaker trial components, default param groups, AWS-managed SSM docs) are excluded by default. Pass include_system_managed=true only if the user specifically asks.\n\nNever:\n- Call check_tag_compliance twice for the same scope.\n- Call get_required_tags before check_tag_compliance — check_tag_compliance resolves the policy itself.\n- Retry a failed tool call — errors here are not transient. Surface the hint/message to the user.\n\nPresenting results:\n- Lead with the compliance percentage and total resource count (when available).\n- ALWAYS state effective_policy.source so the user knows what the scan evaluated against.\n- Surface EVERY field in the response whose name ends in _note or is a caveat (system_managed_note, api_limit_note, global_resources_note, links_truncation_note, accuracy_caveat) verbatim or clearly paraphrased. These are the tool's own warnings — do not omit them.\n- Use top_non_compliant_resource_types / accounts / regions to highlight concentration.\n- Call out worst-offending tag keys when by_required_tag is uneven.\n- If truncated=true, offer to re-run with higher max_resources. If links_truncated=true, offer higher max_links.\n- If status='resource_explorer_not_indexed', explain and suggest enabling Resource Explorer or using use_aws_evaluation=true (if a Tag Policy exists).\n" + }, + "lambda-runtime-upgrade-agent": { + "type": "worker", + "dir": "agents/worker", + "protocol": "http", + "description": "Lambda runtime upgrade assistant — discovers functions on deprecated runtimes, analyzes code compatibility, and provides migration guidance for Python, Node.js, Java, .NET, and Ruby runtime upgrades", + "model": "global.anthropic.claude-sonnet-4-6", + "tools": [ + "lambda-runtime" + ], + "prompt": "You are the Lambda Runtime Upgrade Agent for the CloudOps Multi-Agent Platform.\n\nYou help users upgrade their AWS Lambda function runtimes by discovering outdated functions, analyzing code for compatibility issues, and producing structured migration reports.\n\nTools available via the gateway:\n- discover_lambda_regions: Find all regions with Lambda functions (with counts + deprecated status).\n- list_functions_by_runtime: List functions filtered by runtime/region with status.\n- get_function_configuration: Get detailed function config (layers, handler, memory, env vars).\n- get_function_code: Download function source code for compatibility analysis.\n- get_runtime_support_status: All Lambda runtimes with deprecation/EOL dates.\n- get_deprecated_functions: Find deprecated/EOL functions in a SINGLE region.\n- get_deprecated_functions_multi_region: Find deprecated/EOL functions across MULTIPLE regions in parallel (fast).\n\n═══════════════════════════════════════════════════════════════════\nWORKFLOW\n═══════════════════════════════════════════════════════════════════\n\nSTEP 1 — DETERMINE IF REGIONS ARE SPECIFIED:\n- If the request specifies regions → skip to STEP 3 with those regions.\n- If the request says 'all regions' or 'discover all regions' → call discover_lambda_regions, then use ALL regions with needs_attention > 0. Skip to STEP 3.\n- If the request is a general question like 'which functions are deprecated?' with NO region specified → call discover_lambda_regions, show the user a region table, and ask which regions they want in the report. Wait for their answer.\n\nSTEP 2 — REGION SELECTION (only if user didn't specify):\nShow: Region | Total Functions | Deprecated | EOL | Needs Attention\nAsk: 'Which regions should I include? (all with issues / specific ones)'\n\nSTEP 3 — SCAN:\nCall get_deprecated_functions_multi_region with the selected/specified regions and include_approaching_deprecation=true.\n\nSTEP 4 — REPORT:\nFor speed (target: 5-10 min total):\n- Only call get_function_code for the top 5 CRITICAL/HIGH priority functions.\n- For MEDIUM/LOW functions, provide guidance from the knowledge base WITHOUT downloading code.\n- Skip get_function_configuration unless you need layer/handler details.\n\nSINGLE FUNCTION: If the user asks about ONE specific function, skip everything above — just call get_function_configuration + get_function_code for that function.\n\nCRITICAL: If the request contains 'Execute immediately' or 'Do NOT ask the user', NEVER ask questions — just execute all steps automatically using all regions with issues.\n\n═══════════════════════════════════════════════════════════════════\nREPORT FORMAT\n═══════════════════════════════════════════════════════════════════\n\n## HOW TO USE THIS REPORT\n1. Review the Executive Summary for scope and urgency.\n2. Address CRITICAL priority functions first (EOL runtimes — security risk).\n3. For each function, follow its Migration Checklist.\n4. Test in non-production before deploying.\n5. Update CI/CD pipelines that pin the old runtime.\n6. Verify Lambda layers are compatible with the new runtime.\n7. Monitor CloudWatch metrics post-upgrade.\n\n## EXECUTIVE SUMMARY\n- Total functions requiring upgrade: N\n- By priority: CRITICAL: X | HIGH: Y | MEDIUM: Z\n- Regions scanned: list\n\n## REGION: {region-name}\n### Function: {function-name}\n| Field | Value |\n|-------|-------|\n| Current Runtime | ... |\n| Target Runtime | ... |\n| Priority | CRITICAL / HIGH / MEDIUM |\n| Risk Level | LOW / MEDIUM / HIGH |\n| Estimated Effort | 1-2 hours / half day / 1-2 days |\n\n**Code Changes Required:** (only for CRITICAL/HIGH with code analysis)\n**Library Upgrades:** table\n**Migration Checklist:** checkboxes\n\n═══════════════════════════════════════════════════════════════════\nPRIORITY RULES\n═══════════════════════════════════════════════════════════════════\n\n- CRITICAL: Runtime past End-of-Life. No security patches. Upgrade immediately.\n- HIGH: Runtime deprecated. Limited support.\n- MEDIUM: Deprecation within 6 months. Plan now.\n- LOW: Active but newer LTS exists. Upgrade at convenience.\n\n═══════════════════════════════════════════════════════════════════\nRUNTIME MIGRATION KNOWLEDGE BASE\n═══════════════════════════════════════════════════════════════════\n\nPYTHON (3.8→3.12/3.13):\n- 3.11→3.12: REMOVED modules: distutils, imp, aifc, audioop, cgi, cgitb, chunk, crypt, imghdr, mailcap, msilib, nis, nntplib, ossaudiodev, pipes, sndhdr, spwd, sunau, telnetlib, uu, xdrlib.\n- AWS SDK: boto3/botocore bundled version changes. Pin in requirements.txt.\n\nNODE.JS (16→18→20→22):\n- 16→18: fetch() global. OpenSSL 3.0. AWS SDK v3 required (v2 no longer bundled).\n- AWS SDK MIGRATION: aws-sdk (v2) → @aws-sdk/client-* (v3, modular).\n\nJAVA (8→11→17→21):\n- 8→11: JPMS modules. REMOVED: javax.xml.bind, javax.activation, CORBA.\n- AWS SDK: v1 (com.amazonaws) → v2 (software.amazon.awssdk).\n\n.NET (6→8): System.Text.Json breaking changes. AOT compilation.\n\nRUBY (3.2→3.3): Minimal breaks.\n\nCUSTOM (provided.al2→al2023): glibc 2.34+, OpenSSL 3.0. MUST recompile natives.\n\n═══════════════════════════════════════════════════════════════════\nRULES\n═══════════════════════════════════════════════════════════════════\n\n- Keep output CONCISE — bullet points, not paragraphs.\n- Only list code changes you can VERIFY from actual source code.\n- If code cannot be downloaded (container image), provide generic guidance.\n- Do NOT fabricate issues.\n- Do NOT proactively run extra queries the user didn't ask for.\n" } } diff --git a/src/agents/shared/report_templates/lambda_runtime_upgrade_report.json b/src/agents/shared/report_templates/lambda_runtime_upgrade_report.json new file mode 100644 index 0000000..833e9d3 --- /dev/null +++ b/src/agents/shared/report_templates/lambda_runtime_upgrade_report.json @@ -0,0 +1,19 @@ +{ + "name": "Lambda Runtime Upgrade Report", + "description": "Comprehensive assessment of AWS Lambda functions requiring runtime upgrades, organized by region and priority. Covers deprecated/EOL runtime discovery, code compatibility analysis, and step-by-step migration guidance. Routes to the Ops Excellence domain (lambda-runtime-upgrade leaf agent).", + "sections": [ + { + "id": "discovery_and_summary", + "title": "Runtime Discovery & Executive Summary", + "prompt": "I need a Lambda runtime upgrade assessment. Use the lambda runtime tools to discover deprecated Lambda functions across all regions.\n\nDo these steps:\n1. Call discover_lambda_regions to find which regions have Lambda functions needing upgrades.\n2. For all regions where needs_attention > 0, call get_deprecated_functions_multi_region with those regions and include_approaching_deprecation=true.\n3. Present the results as:\n\n## How to Use This Report\n1. Review the Executive Summary for scope and urgency.\n2. Address CRITICAL priority functions first (EOL = security risk).\n3. For each function, follow the Migration Checklist in the next section.\n4. Test each upgraded function in non-production before deploying.\n5. Update CI/CD pipelines that pin the old runtime.\n6. Verify Lambda layers are compatible with the new runtime.\n7. Monitor CloudWatch metrics post-upgrade.\n\n## Executive Summary\n- Total functions requiring upgrade\n- Priority breakdown: CRITICAL (EOL) | HIGH (deprecated) | MEDIUM (approaching)\n- Regions affected\n- Runtime families affected\n\n## By Region\nTable: Region | CRITICAL | HIGH | MEDIUM | Total\n\n## By Runtime\nTable: Runtime | Status | Target Runtime | Count\n\n## Full Function Inventory\nFor each region with issues, a table: Function Name | Current Runtime | Target Runtime | Priority | Last Modified\nSort by priority (CRITICAL first), then oldest first.\n\nDo NOT ask the user anything. Do NOT include follow-up questions. Execute immediately." + }, + { + "id": "code_analysis", + "title": "Code Compatibility Analysis & Migration Guidance", + "prompt": "I need Lambda runtime code compatibility analysis. Use the lambda runtime tools to find and analyze deprecated functions.\n\nDo these steps:\n1. Call get_deprecated_functions_multi_region with regions=['us-east-1','us-west-2','eu-west-1','eu-central-1','ap-southeast-1','ap-northeast-1'] and include_approaching_deprecation=true to find all deprecated functions.\n2. From the results, identify the top 5 functions with the worst status (end_of_life first, then deprecated, sorted by oldest last_modified).\n3. For each of those top 5 functions, call get_function_configuration to get its setup, then call get_function_code to download its source code.\n4. Analyze the downloaded code for breaking changes between current and target runtime.\n\nPresent for each of the top 5 functions:\n\n### Function: {name} ({region})\n| Field | Value |\n|-------|-------|\n| Current Runtime | ... |\n| Target Runtime | ... |\n| Priority | CRITICAL/HIGH |\n| Risk Level | LOW/MEDIUM/HIGH |\n| Estimated Effort | 1-2 hours / half day / 1-2 days |\n\n**Code Changes Required:**\n(Show actual before/after snippets from the downloaded source code. If no issues found, say: No breaking changes — safe to upgrade runtime directly.)\n\n**Library Upgrades:**\n| Library | Current | Required | Reason |\n\n**Migration Checklist:**\n- [ ] Update runtime setting\n- [ ] Apply code changes above\n- [ ] Update dependencies\n- [ ] Update Lambda layers\n- [ ] Test in non-prod\n- [ ] Deploy and monitor 48h\n\nIf get_function_code fails for a function (e.g. container image), state that and provide generic guidance based on the runtime migration knowledge base.\n\nAfter the top 5, add:\n\n## Migration Playbook\n- Phase 1 (This Week): CRITICAL functions\n- Phase 2 (This Month): HIGH functions\n- Phase 3 (This Quarter): MEDIUM functions\n\n## Common Pitfalls\n(List 3-5 pitfalls specific to the runtime families found)\n\n## Rollback Plan\n- Use Lambda versioning and aliases\n- Set CloudWatch error rate alarms before switching\n- Keep old version as $LATEST-1 for instant rollback\n\nDo NOT ask the user anything. Do NOT include follow-up questions. Execute immediately." + } + ], + "dependencies": { + "code_analysis": "discovery_and_summary" + } +} diff --git a/src/lambda/frontend/core-api/report_templates/lambda_runtime_upgrade_report.json b/src/lambda/frontend/core-api/report_templates/lambda_runtime_upgrade_report.json new file mode 100644 index 0000000..833e9d3 --- /dev/null +++ b/src/lambda/frontend/core-api/report_templates/lambda_runtime_upgrade_report.json @@ -0,0 +1,19 @@ +{ + "name": "Lambda Runtime Upgrade Report", + "description": "Comprehensive assessment of AWS Lambda functions requiring runtime upgrades, organized by region and priority. Covers deprecated/EOL runtime discovery, code compatibility analysis, and step-by-step migration guidance. Routes to the Ops Excellence domain (lambda-runtime-upgrade leaf agent).", + "sections": [ + { + "id": "discovery_and_summary", + "title": "Runtime Discovery & Executive Summary", + "prompt": "I need a Lambda runtime upgrade assessment. Use the lambda runtime tools to discover deprecated Lambda functions across all regions.\n\nDo these steps:\n1. Call discover_lambda_regions to find which regions have Lambda functions needing upgrades.\n2. For all regions where needs_attention > 0, call get_deprecated_functions_multi_region with those regions and include_approaching_deprecation=true.\n3. Present the results as:\n\n## How to Use This Report\n1. Review the Executive Summary for scope and urgency.\n2. Address CRITICAL priority functions first (EOL = security risk).\n3. For each function, follow the Migration Checklist in the next section.\n4. Test each upgraded function in non-production before deploying.\n5. Update CI/CD pipelines that pin the old runtime.\n6. Verify Lambda layers are compatible with the new runtime.\n7. Monitor CloudWatch metrics post-upgrade.\n\n## Executive Summary\n- Total functions requiring upgrade\n- Priority breakdown: CRITICAL (EOL) | HIGH (deprecated) | MEDIUM (approaching)\n- Regions affected\n- Runtime families affected\n\n## By Region\nTable: Region | CRITICAL | HIGH | MEDIUM | Total\n\n## By Runtime\nTable: Runtime | Status | Target Runtime | Count\n\n## Full Function Inventory\nFor each region with issues, a table: Function Name | Current Runtime | Target Runtime | Priority | Last Modified\nSort by priority (CRITICAL first), then oldest first.\n\nDo NOT ask the user anything. Do NOT include follow-up questions. Execute immediately." + }, + { + "id": "code_analysis", + "title": "Code Compatibility Analysis & Migration Guidance", + "prompt": "I need Lambda runtime code compatibility analysis. Use the lambda runtime tools to find and analyze deprecated functions.\n\nDo these steps:\n1. Call get_deprecated_functions_multi_region with regions=['us-east-1','us-west-2','eu-west-1','eu-central-1','ap-southeast-1','ap-northeast-1'] and include_approaching_deprecation=true to find all deprecated functions.\n2. From the results, identify the top 5 functions with the worst status (end_of_life first, then deprecated, sorted by oldest last_modified).\n3. For each of those top 5 functions, call get_function_configuration to get its setup, then call get_function_code to download its source code.\n4. Analyze the downloaded code for breaking changes between current and target runtime.\n\nPresent for each of the top 5 functions:\n\n### Function: {name} ({region})\n| Field | Value |\n|-------|-------|\n| Current Runtime | ... |\n| Target Runtime | ... |\n| Priority | CRITICAL/HIGH |\n| Risk Level | LOW/MEDIUM/HIGH |\n| Estimated Effort | 1-2 hours / half day / 1-2 days |\n\n**Code Changes Required:**\n(Show actual before/after snippets from the downloaded source code. If no issues found, say: No breaking changes — safe to upgrade runtime directly.)\n\n**Library Upgrades:**\n| Library | Current | Required | Reason |\n\n**Migration Checklist:**\n- [ ] Update runtime setting\n- [ ] Apply code changes above\n- [ ] Update dependencies\n- [ ] Update Lambda layers\n- [ ] Test in non-prod\n- [ ] Deploy and monitor 48h\n\nIf get_function_code fails for a function (e.g. container image), state that and provide generic guidance based on the runtime migration knowledge base.\n\nAfter the top 5, add:\n\n## Migration Playbook\n- Phase 1 (This Week): CRITICAL functions\n- Phase 2 (This Month): HIGH functions\n- Phase 3 (This Quarter): MEDIUM functions\n\n## Common Pitfalls\n(List 3-5 pitfalls specific to the runtime families found)\n\n## Rollback Plan\n- Use Lambda versioning and aliases\n- Set CloudWatch error rate alarms before switching\n- Keep old version as $LATEST-1 for instant rollback\n\nDo NOT ask the user anything. Do NOT include follow-up questions. Execute immediately." + } + ], + "dependencies": { + "code_analysis": "discovery_and_summary" + } +} diff --git a/src/lambda/mcp/lambda-runtime/handler.py b/src/lambda/mcp/lambda-runtime/handler.py new file mode 100644 index 0000000..b00056d --- /dev/null +++ b/src/lambda/mcp/lambda-runtime/handler.py @@ -0,0 +1,627 @@ +""" +AWS Lambda Runtime Upgrade MCP Tool — Lambda Implementation for AgentCore Gateway + +Provides tools for discovering Lambda functions with outdated runtimes, +retrieving function code for compatibility analysis, and checking runtime +support/deprecation status. + +Tools (7): +- discover_lambda_regions: Find all regions that have Lambda functions (with counts) +- list_functions_by_runtime: List Lambda functions filtered by runtime/region +- get_function_configuration: Get detailed function config (runtime, layers, handler) +- get_function_code: Download and return function source code for analysis +- get_runtime_support_status: Show all Lambda runtimes with deprecation/EOL dates +- get_deprecated_functions: Find all functions using deprecated or EOL runtimes +- get_deprecated_functions_multi_region: Scan multiple regions in parallel for deprecated functions + +Required IAM Permissions: +- lambda:ListFunctions +- lambda:GetFunction +- lambda:GetFunctionConfiguration +- ec2:DescribeRegions +""" + +import json +import os +import zipfile +from concurrent.futures import ThreadPoolExecutor, as_completed +from datetime import datetime, timezone +from io import BytesIO +from typing import Optional + +import boto3 +import urllib3 + + +def handler(event, context): + print(f"Event: {json.dumps(event)}") + extended_tool_name = context.client_context.custom["bedrockAgentCoreToolName"] + tool_name = extended_tool_name.split("___")[1] + print(f"Tool name: {tool_name}") + + handlers = { + "discover_lambda_regions": handle_discover_lambda_regions, + "list_functions_by_runtime": handle_list_functions_by_runtime, + "get_function_configuration": handle_get_function_configuration, + "get_function_code": handle_get_function_code, + "get_runtime_support_status": handle_get_runtime_support_status, + "get_deprecated_functions": handle_get_deprecated_functions, + "get_deprecated_functions_multi_region": handle_get_deprecated_functions_multi_region, + } + fn = handlers.get(tool_name) + if fn: + response = fn(event) + print(f"Response: {json.dumps(response, default=str)}") + return response + return { + "error": f"Unknown tool: {tool_name}", + "available_tools": list(handlers.keys()), + } + + +# --------------------------------------------------------------------------- +# Runtime support status data (updated periodically) +# --------------------------------------------------------------------------- + +RUNTIME_SUPPORT_DATA = { + # Python runtimes + "python3.8": {"family": "python", "version": "3.8", "status": "deprecated", + "deprecation_date": "2024-10-14", "eol_date": "2025-02-28", + "upgrade_target": "python3.12"}, + "python3.9": {"family": "python", "version": "3.9", "status": "active", + "deprecation_date": "2025-09-01", "eol_date": None, + "upgrade_target": "python3.12"}, + "python3.10": {"family": "python", "version": "3.10", "status": "active", + "deprecation_date": "2026-06-01", "eol_date": None, + "upgrade_target": "python3.13"}, + "python3.11": {"family": "python", "version": "3.11", "status": "active", + "deprecation_date": "2026-12-01", "eol_date": None, + "upgrade_target": "python3.13"}, + "python3.12": {"family": "python", "version": "3.12", "status": "active", + "deprecation_date": None, "eol_date": None, + "upgrade_target": "python3.13"}, + "python3.13": {"family": "python", "version": "3.13", "status": "active", + "deprecation_date": None, "eol_date": None, + "upgrade_target": None}, + # Node.js runtimes + "nodejs14.x": {"family": "nodejs", "version": "14", "status": "deprecated", + "deprecation_date": "2023-12-04", "eol_date": "2024-03-11", + "upgrade_target": "nodejs20.x"}, + "nodejs16.x": {"family": "nodejs", "version": "16", "status": "deprecated", + "deprecation_date": "2024-06-12", "eol_date": "2024-09-11", + "upgrade_target": "nodejs20.x"}, + "nodejs18.x": {"family": "nodejs", "version": "18", "status": "active", + "deprecation_date": "2025-09-01", "eol_date": None, + "upgrade_target": "nodejs22.x"}, + "nodejs20.x": {"family": "nodejs", "version": "20", "status": "active", + "deprecation_date": "2026-06-01", "eol_date": None, + "upgrade_target": "nodejs22.x"}, + "nodejs22.x": {"family": "nodejs", "version": "22", "status": "active", + "deprecation_date": None, "eol_date": None, + "upgrade_target": None}, + # Java runtimes + "java8": {"family": "java", "version": "8", "status": "deprecated", + "deprecation_date": "2024-01-08", "eol_date": "2024-04-08", + "upgrade_target": "java21"}, + "java8.al2": {"family": "java", "version": "8 (AL2)", "status": "deprecated", + "deprecation_date": "2024-08-01", "eol_date": "2024-11-01", + "upgrade_target": "java21"}, + "java11": {"family": "java", "version": "11", "status": "active", + "deprecation_date": "2025-09-01", "eol_date": None, + "upgrade_target": "java21"}, + "java17": {"family": "java", "version": "17", "status": "active", + "deprecation_date": "2026-09-01", "eol_date": None, + "upgrade_target": "java21"}, + "java21": {"family": "java", "version": "21", "status": "active", + "deprecation_date": None, "eol_date": None, + "upgrade_target": None}, + # .NET runtimes + "dotnet6": {"family": "dotnet", "version": "6", "status": "deprecated", + "deprecation_date": "2024-02-29", "eol_date": "2024-05-29", + "upgrade_target": "dotnet8"}, + "dotnet8": {"family": "dotnet", "version": "8", "status": "active", + "deprecation_date": None, "eol_date": None, + "upgrade_target": None}, + # Ruby runtimes + "ruby3.2": {"family": "ruby", "version": "3.2", "status": "active", + "deprecation_date": "2026-03-01", "eol_date": None, + "upgrade_target": "ruby3.3"}, + "ruby3.3": {"family": "ruby", "version": "3.3", "status": "active", + "deprecation_date": None, "eol_date": None, + "upgrade_target": None}, + # Go (provided.al2 is the custom runtime for Go) + "provided.al2": {"family": "custom", "version": "AL2", "status": "active", + "deprecation_date": "2025-09-01", "eol_date": None, + "upgrade_target": "provided.al2023"}, + "provided.al2023": {"family": "custom", "version": "AL2023", "status": "active", + "deprecation_date": None, "eol_date": None, + "upgrade_target": None}, +} + + +def _get_lambda_client(region: Optional[str] = None): + """Get Lambda client, optionally for a specific region.""" + from shared.cross_account import get_aws_client + return get_aws_client("lambda", region_name=region) + + +def _classify_runtime(runtime_id: str) -> dict: + """Classify a runtime's support status.""" + info = RUNTIME_SUPPORT_DATA.get(runtime_id) + if not info: + return {"status": "unknown", "runtime": runtime_id} + now = datetime.now(timezone.utc).strftime("%Y-%m-%d") + effective_status = info["status"] + if info.get("eol_date") and now > info["eol_date"]: + effective_status = "end_of_life" + elif info.get("deprecation_date") and now > info["deprecation_date"]: + effective_status = "deprecated" + return { + "runtime": runtime_id, + "family": info["family"], + "version": info["version"], + "status": effective_status, + "deprecation_date": info.get("deprecation_date"), + "eol_date": info.get("eol_date"), + "upgrade_target": info.get("upgrade_target"), + } + + +def handle_discover_lambda_regions(event): + """Discover which AWS regions have Lambda functions deployed. + + Scans all enabled regions in parallel to find which ones contain + Lambda functions. Returns region name, function count, and a breakdown + of deprecated/EOL functions per region so the user can choose which + regions to include in a detailed report. + """ + try: + from shared.cross_account import get_aws_client + # Get all enabled regions + ec2_client = get_aws_client("ec2", region_name="us-east-1") + regions_response = ec2_client.describe_regions( + Filters=[{"Name": "opt-in-status", "Values": ["opt-in-not-required", "opted-in"]}] + ) + all_regions = [r["RegionName"] for r in regions_response.get("Regions", [])] + + def _scan_region(region_name): + """Count functions in a single region.""" + try: + client = get_aws_client("lambda", region_name=region_name) + total = 0 + deprecated_count = 0 + eol_count = 0 + runtimes_found = {} + paginator = client.get_paginator("list_functions") + for page in paginator.paginate(): + for fn in page.get("Functions", []): + total += 1 + runtime = fn.get("Runtime", "") + if runtime: + runtimes_found[runtime] = runtimes_found.get(runtime, 0) + 1 + info = _classify_runtime(runtime) + if info["status"] == "deprecated": + deprecated_count += 1 + elif info["status"] == "end_of_life": + eol_count += 1 + return { + "region": region_name, + "total_functions": total, + "deprecated_count": deprecated_count, + "eol_count": eol_count, + "needs_attention": deprecated_count + eol_count, + "runtimes": runtimes_found, + } + except Exception as e: + return {"region": region_name, "total_functions": 0, "error": str(e)} + + # Scan all regions in parallel (max 10 threads) + results = [] + with ThreadPoolExecutor(max_workers=10) as executor: + futures = {executor.submit(_scan_region, r): r for r in all_regions} + for future in as_completed(futures): + result = future.result() + if result.get("total_functions", 0) > 0: + results.append(result) + + # Sort by needs_attention (deprecated + EOL) desc, then total desc + results.sort(key=lambda r: (-r.get("needs_attention", 0), -r.get("total_functions", 0))) + + total_functions = sum(r["total_functions"] for r in results) + total_deprecated = sum(r.get("deprecated_count", 0) for r in results) + total_eol = sum(r.get("eol_count", 0) for r in results) + + return { + "regions_with_functions": results, + "total_regions": len(results), + "total_functions": total_functions, + "total_deprecated": total_deprecated, + "total_eol": total_eol, + "total_needing_attention": total_deprecated + total_eol, + "note": "Select the regions you want included in the detailed upgrade report.", + } + except Exception as e: + return {"error": str(e)} + + +def handle_list_functions_by_runtime(event): + """List Lambda functions, optionally filtered by runtime or region.""" + region = event.get("region") + runtime_filter = event.get("runtime") + max_results = min(event.get("max_results", 100), 500) + + try: + client = _get_lambda_client(region) + functions = [] + paginator = client.get_paginator("list_functions") + + for page in paginator.paginate(): + for fn in page.get("Functions", []): + runtime = fn.get("Runtime", "") + if runtime_filter and runtime != runtime_filter: + continue + runtime_info = _classify_runtime(runtime) + functions.append({ + "function_name": fn["FunctionName"], + "runtime": runtime, + "runtime_status": runtime_info["status"], + "upgrade_target": runtime_info.get("upgrade_target"), + "handler": fn.get("Handler", ""), + "last_modified": fn.get("LastModified", ""), + "memory_mb": fn.get("MemorySize", 0), + "code_size_bytes": fn.get("CodeSize", 0), + "architecture": fn.get("Architectures", ["x86_64"]), + }) + if len(functions) >= max_results: + break + if len(functions) >= max_results: + break + + return { + "functions": functions, + "count": len(functions), + "region": region or os.environ.get("AWS_REGION", "us-east-1"), + "filter_applied": {"runtime": runtime_filter} if runtime_filter else None, + } + except Exception as e: + return {"error": str(e)} + + +def handle_get_function_configuration(event): + """Get detailed configuration for a specific Lambda function.""" + function_name = event.get("function_name") + region = event.get("region") + + if not function_name: + return {"error": "function_name is required"} + + try: + client = _get_lambda_client(region) + config = client.get_function_configuration(FunctionName=function_name) + runtime = config.get("Runtime", "") + runtime_info = _classify_runtime(runtime) + + layers = [] + for layer in config.get("Layers", []): + layers.append({ + "arn": layer.get("Arn", ""), + "code_size": layer.get("CodeSize", 0), + }) + + return { + "function_name": config["FunctionName"], + "function_arn": config["FunctionArn"], + "runtime": runtime, + "runtime_status": runtime_info, + "handler": config.get("Handler", ""), + "code_size_bytes": config.get("CodeSize", 0), + "memory_mb": config.get("MemorySize", 128), + "timeout_seconds": config.get("Timeout", 3), + "last_modified": config.get("LastModified", ""), + "architecture": config.get("Architectures", ["x86_64"]), + "layers": layers, + "environment_variables": list( + config.get("Environment", {}).get("Variables", {}).keys() + ), + "package_type": config.get("PackageType", "Zip"), + "ephemeral_storage_mb": config.get( + "EphemeralStorage", {} + ).get("Size", 512), + } + except Exception as e: + return {"error": str(e)} + + +def handle_get_function_code(event): + """Download and return the function's source code for compatibility analysis. + + Returns the contents of .py, .js, .ts, .java, .cs, .rb, .go files + from the deployment package (up to a size limit to avoid overwhelming + the agent context). + """ + function_name = event.get("function_name") + region = event.get("region") + max_file_size_kb = event.get("max_file_size_kb", 50) + include_patterns = event.get("include_patterns") + + if not function_name: + return {"error": "function_name is required"} + + CODE_EXTENSIONS = { + ".py", ".js", ".ts", ".mjs", ".cjs", + ".java", ".cs", ".rb", ".go", + ".json", ".yaml", ".yml", ".toml", ".cfg", ".txt", + } + # Dependency/config files worth including + DEP_FILES = { + "requirements.txt", "package.json", "pom.xml", "build.gradle", + "Gemfile", "go.mod", "go.sum", ".csproj", + } + + try: + client = _get_lambda_client(region) + response = client.get_function(FunctionName=function_name) + code_location = response.get("Code", {}).get("Location") + + if not code_location: + return {"error": "Unable to retrieve function code location"} + + # Download the zip + http = urllib3.PoolManager() + resp = http.request("GET", code_location) + if resp.status != 200: + return {"error": f"Failed to download code: HTTP {resp.status}"} + + zip_data = BytesIO(resp.data) + files = {} + total_size = 0 + max_total_kb = 200 # Cap total extracted content + + with zipfile.ZipFile(zip_data, "r") as zf: + for info in zf.infolist(): + if info.is_dir(): + continue + filename = info.filename + ext = os.path.splitext(filename)[1].lower() + basename = os.path.basename(filename) + + # Skip node_modules, __pycache__, .git, vendor dirs + skip_dirs = {"node_modules/", "__pycache__/", ".git/", + "vendor/", "venv/", ".venv/", "site-packages/"} + if any(d in filename for d in skip_dirs): + continue + + # Include code files and dependency manifests + is_code = ext in CODE_EXTENSIONS + is_dep = basename in DEP_FILES + if not is_code and not is_dep: + continue + + # Apply include_patterns filter if provided + if include_patterns: + matched = any(p in filename for p in include_patterns) + if not matched: + continue + + # Size guard per file + if info.file_size > max_file_size_kb * 1024: + files[filename] = f"[SKIPPED: {info.file_size // 1024}KB exceeds limit]" + continue + + # Total size guard + if total_size + info.file_size > max_total_kb * 1024: + files[filename] = f"[SKIPPED: total extraction limit reached]" + continue + + try: + content = zf.read(info.filename).decode("utf-8", errors="replace") + files[filename] = content + total_size += len(content) + except Exception: + files[filename] = "[SKIPPED: binary or unreadable]" + + return { + "function_name": function_name, + "files_extracted": len(files), + "total_size_kb": round(total_size / 1024, 1), + "source_files": files, + } + except Exception as e: + return {"error": str(e)} + + +def handle_get_runtime_support_status(event): + """Return all Lambda runtimes with their support/deprecation status.""" + family_filter = event.get("family") + + runtimes = [] + for runtime_id, info in RUNTIME_SUPPORT_DATA.items(): + if family_filter and info["family"] != family_filter: + continue + classified = _classify_runtime(runtime_id) + runtimes.append(classified) + + # Sort: deprecated/EOL first, then by family + status_order = {"end_of_life": 0, "deprecated": 1, "active": 2, "unknown": 3} + runtimes.sort(key=lambda r: (status_order.get(r["status"], 3), r["family"], r["runtime"])) + + summary = { + "active": sum(1 for r in runtimes if r["status"] == "active"), + "deprecated": sum(1 for r in runtimes if r["status"] == "deprecated"), + "end_of_life": sum(1 for r in runtimes if r["status"] == "end_of_life"), + } + + return { + "runtimes": runtimes, + "total": len(runtimes), + "summary": summary, + "note": "Dates are approximate and based on AWS published schedules. " + "Check AWS documentation for latest updates.", + } + + +def handle_get_deprecated_functions(event): + """Find all functions using deprecated or end-of-life runtimes.""" + region = event.get("region") + include_approaching = event.get("include_approaching_deprecation", False) + max_results = min(event.get("max_results", 200), 500) + + try: + client = _get_lambda_client(region) + deprecated_functions = [] + paginator = client.get_paginator("list_functions") + + for page in paginator.paginate(): + for fn in page.get("Functions", []): + runtime = fn.get("Runtime", "") + if not runtime: + continue + runtime_info = _classify_runtime(runtime) + status = runtime_info.get("status", "unknown") + + include = status in ("deprecated", "end_of_life") + if include_approaching and status == "active": + dep_date = runtime_info.get("deprecation_date") + if dep_date: + now = datetime.now(timezone.utc).strftime("%Y-%m-%d") + # Include if deprecation is within 6 months + from datetime import timedelta + threshold = ( + datetime.now(timezone.utc) + timedelta(days=180) + ).strftime("%Y-%m-%d") + if dep_date <= threshold: + include = True + + if include: + deprecated_functions.append({ + "function_name": fn["FunctionName"], + "runtime": runtime, + "runtime_status": status, + "deprecation_date": runtime_info.get("deprecation_date"), + "eol_date": runtime_info.get("eol_date"), + "upgrade_target": runtime_info.get("upgrade_target"), + "last_modified": fn.get("LastModified", ""), + "code_size_bytes": fn.get("CodeSize", 0), + }) + if len(deprecated_functions) >= max_results: + break + if len(deprecated_functions) >= max_results: + break + + # Group by runtime for summary + by_runtime = {} + for fn in deprecated_functions: + rt = fn["runtime"] + if rt not in by_runtime: + by_runtime[rt] = {"count": 0, "status": fn["runtime_status"], + "upgrade_target": fn["upgrade_target"]} + by_runtime[rt]["count"] += 1 + + return { + "deprecated_functions": deprecated_functions, + "count": len(deprecated_functions), + "by_runtime": by_runtime, + "region": region or os.environ.get("AWS_REGION", "us-east-1"), + "include_approaching_deprecation": include_approaching, + } + except Exception as e: + return {"error": str(e)} + + +def handle_get_deprecated_functions_multi_region(event): + """Find deprecated/EOL functions across multiple regions in parallel. + + This is the fast-path for report generation: scans all specified regions + concurrently instead of requiring sequential per-region calls. + """ + regions = event.get("regions", []) + include_approaching = event.get("include_approaching_deprecation", False) + max_results_per_region = min(event.get("max_results_per_region", 100), 500) + + if not regions: + return {"error": "regions array is required (e.g., ['us-east-1', 'eu-west-1'])"} + + def _scan_region(region): + """Scan a single region for deprecated functions.""" + try: + client = _get_lambda_client(region) + deprecated_functions = [] + paginator = client.get_paginator("list_functions") + + for page in paginator.paginate(): + for fn in page.get("Functions", []): + runtime = fn.get("Runtime", "") + if not runtime: + continue + runtime_info = _classify_runtime(runtime) + status = runtime_info.get("status", "unknown") + + include = status in ("deprecated", "end_of_life") + if include_approaching and status == "active": + dep_date = runtime_info.get("deprecation_date") + if dep_date: + from datetime import timedelta + threshold = ( + datetime.now(timezone.utc) + timedelta(days=180) + ).strftime("%Y-%m-%d") + if dep_date <= threshold: + include = True + + if include: + deprecated_functions.append({ + "function_name": fn["FunctionName"], + "runtime": runtime, + "runtime_status": status, + "deprecation_date": runtime_info.get("deprecation_date"), + "eol_date": runtime_info.get("eol_date"), + "upgrade_target": runtime_info.get("upgrade_target"), + "last_modified": fn.get("LastModified", ""), + "code_size_bytes": fn.get("CodeSize", 0), + "handler": fn.get("Handler", ""), + "memory_mb": fn.get("MemorySize", 0), + }) + if len(deprecated_functions) >= max_results_per_region: + break + if len(deprecated_functions) >= max_results_per_region: + break + + return {"region": region, "functions": deprecated_functions, "count": len(deprecated_functions)} + except Exception as e: + return {"region": region, "functions": [], "count": 0, "error": str(e)} + + # Scan all requested regions in parallel + all_results = [] + with ThreadPoolExecutor(max_workers=min(len(regions), 10)) as executor: + futures = {executor.submit(_scan_region, r): r for r in regions} + for future in as_completed(futures): + all_results.append(future.result()) + + # Sort results by region name for consistent output + all_results.sort(key=lambda r: r["region"]) + + # Build summary + total_functions = sum(r["count"] for r in all_results) + by_runtime = {} + by_priority = {"end_of_life": 0, "deprecated": 0, "approaching": 0} + for region_result in all_results: + for fn in region_result["functions"]: + rt = fn["runtime"] + if rt not in by_runtime: + by_runtime[rt] = {"count": 0, "status": fn["runtime_status"], + "upgrade_target": fn["upgrade_target"]} + by_runtime[rt]["count"] += 1 + if fn["runtime_status"] == "end_of_life": + by_priority["end_of_life"] += 1 + elif fn["runtime_status"] == "deprecated": + by_priority["deprecated"] += 1 + else: + by_priority["approaching"] += 1 + + return { + "results_by_region": all_results, + "total_functions": total_functions, + "regions_scanned": len(regions), + "by_runtime": by_runtime, + "by_priority": by_priority, + "include_approaching_deprecation": include_approaching, + } diff --git a/src/lambda/mcp/lambda-runtime/requirements.txt b/src/lambda/mcp/lambda-runtime/requirements.txt new file mode 100644 index 0000000..b737506 --- /dev/null +++ b/src/lambda/mcp/lambda-runtime/requirements.txt @@ -0,0 +1,9 @@ +# Empty — Lambda Python 3.12 runtime provides boto3/botocore at runtime. +# urllib3 is also bundled with botocore. +# +# When adding a dep, ALWAYS pin to a specific version (==X.Y.Z), not a +# floor (>=X.Y) or unconstrained name. The build packages this file via +# `pip install -r requirements.txt -t ./package/` — without pins, every +# rebuild can pull a newer minor that introduces breaking changes (we +# hit this with ag-ui-strands 0.1.4 → 0.1.9 silently changing default +# history-replay behavior). Bump deliberately, retest, then update. diff --git a/src/lambda/mcp/tools.json b/src/lambda/mcp/tools.json index c82c444..b4fec70 100644 --- a/src/lambda/mcp/tools.json +++ b/src/lambda/mcp/tools.json @@ -1135,5 +1135,153 @@ } } ] + }, + "lambda-runtime": { + "handler": "handler.handler", + "runtime": "python3.12", + "timeout": 120, + "memory": 512, + "env_vars": { + "CROSS_ACCOUNT_ROLE_ARN": "$CROSS_ACCOUNT_ROLE_ARN" + }, + "iam_actions": [ + "lambda:ListFunctions", + "lambda:GetFunction", + "lambda:GetFunctionConfiguration", + "ec2:DescribeRegions" + ], + "tools": [ + { + "name": "discover_lambda_regions", + "description": "Discover which AWS regions have Lambda functions deployed. Scans all enabled regions in parallel and returns each region's function count plus how many are on deprecated or end-of-life runtimes. Use this FIRST to let the user choose which regions to include in a report.", + "input_schema": { + "type": "object", + "properties": {} + } + }, + { + "name": "list_functions_by_runtime", + "description": "List Lambda functions with their runtime status. Optionally filter by runtime identifier or region. Returns function name, current runtime, support status, and recommended upgrade target.", + "input_schema": { + "type": "object", + "properties": { + "runtime": { + "type": "string", + "description": "Filter by specific runtime (e.g., python3.9, nodejs18.x, java11)" + }, + "region": { + "type": "string", + "description": "AWS region to scan (default: current region)" + }, + "max_results": { + "type": "integer", + "description": "Maximum functions to return (default: 100, max: 500)" + } + } + } + }, + { + "name": "get_function_configuration", + "description": "Get detailed configuration for a specific Lambda function including runtime, handler, layers, memory, timeout, architecture, and environment variable keys. Use this before code analysis to understand the function's setup.", + "input_schema": { + "type": "object", + "properties": { + "function_name": { + "type": "string", + "description": "Lambda function name or ARN" + }, + "region": { + "type": "string", + "description": "AWS region (optional)" + } + }, + "required": ["function_name"] + } + }, + { + "name": "get_function_code", + "description": "Download and return the source code files from a Lambda function's deployment package. Extracts .py, .js, .ts, .java, .cs, .rb, .go files plus dependency manifests (requirements.txt, package.json, pom.xml, etc.). Skips node_modules, __pycache__, and vendor directories. Use this to analyze code for runtime compatibility issues.", + "input_schema": { + "type": "object", + "properties": { + "function_name": { + "type": "string", + "description": "Lambda function name or ARN" + }, + "region": { + "type": "string", + "description": "AWS region (optional)" + }, + "max_file_size_kb": { + "type": "integer", + "description": "Max size per file in KB (default: 50). Files exceeding this are skipped." + }, + "include_patterns": { + "type": "array", + "items": {"type": "string"}, + "description": "Only include files matching these substrings (e.g., ['handler', 'main'])" + } + }, + "required": ["function_name"] + } + }, + { + "name": "get_runtime_support_status", + "description": "Get support status for all AWS Lambda runtimes including active, deprecated, and end-of-life runtimes with their deprecation dates and recommended upgrade targets. Optionally filter by runtime family.", + "input_schema": { + "type": "object", + "properties": { + "family": { + "type": "string", + "description": "Filter by runtime family: python, nodejs, java, dotnet, ruby, custom" + } + } + } + }, + { + "name": "get_deprecated_functions", + "description": "Find all Lambda functions using deprecated or end-of-life runtimes in a SINGLE region. Returns functions grouped by runtime with upgrade recommendations. For multi-region scanning, use get_deprecated_functions_multi_region instead.", + "input_schema": { + "type": "object", + "properties": { + "region": { + "type": "string", + "description": "AWS region to scan (default: current region)" + }, + "include_approaching_deprecation": { + "type": "boolean", + "description": "Include functions with runtimes approaching deprecation within 6 months (default: false)" + }, + "max_results": { + "type": "integer", + "description": "Maximum functions to return (default: 200, max: 500)" + } + } + } + }, + { + "name": "get_deprecated_functions_multi_region", + "description": "Find all Lambda functions using deprecated or end-of-life runtimes across MULTIPLE regions in parallel. Much faster than calling get_deprecated_functions repeatedly. Use this for multi-region upgrade reports.", + "input_schema": { + "type": "object", + "properties": { + "regions": { + "type": "array", + "items": {"type": "string"}, + "description": "List of AWS regions to scan (e.g., ['us-east-1', 'eu-west-1', 'ap-southeast-1'])" + }, + "include_approaching_deprecation": { + "type": "boolean", + "description": "Include functions with runtimes approaching deprecation within 6 months (default: false)" + }, + "max_results_per_region": { + "type": "integer", + "description": "Maximum functions per region (default: 100, max: 500)" + } + }, + "required": ["regions"] + } + } + ] } }