From d1b06afe84d4c5184f2f777b89637f5594d2a1b4 Mon Sep 17 00:00:00 2001 From: Paul Pollack Date: Wed, 1 Jul 2026 13:29:41 -0400 Subject: [PATCH 1/8] feat(gcp-to-aws): add Elastic Beanstalk as migration target for App Engine workloads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add AWS Elastic Beanstalk as a PaaS-to-PaaS mapping for Google App Engine resources. App Engine Standard/Flexible now maps to EB by default, with user override to Fargate or Lambda via a new Clarify question (Q7b). Key decisions: - App Engine → EB is a conditional fast-path (deterministic when compute_model is absent or "managed_platform") - Cloud Run remains unconditionally mapped to Fargate (unaffected) - Q7b only fires when App Engine is in inventory - EB design reference provides platform detection, configuration, deployment policies, and App Engine feature mapping - google_app_engine_standard/flexible_app_version added to Skip Mappings Also fixes pre-existing inaccuracies in compute.md examples where Cloud Run and Cloud Functions were incorrectly shown as rubric-evaluated (they're deterministic fast-path mappings). --- .../skills/gcp-to-aws/SKILL.md | 4 +- .../references/design-refs/compute.md | 38 ++++- .../design-refs/elastic-beanstalk.md | 135 ++++++++++++++++++ .../references/design-refs/fast-path.md | 6 + .../references/design-refs/index.md | 2 +- .../phases/clarify/clarify-compute.md | 37 ++++- .../references/phases/clarify/clarify.md | 10 +- .../references/phases/design/design-infra.md | 9 +- .../references/phases/design/design.md | 1 + .../references/shared/pricing-cache.md | 15 ++ 10 files changed, 245 insertions(+), 12 deletions(-) create mode 100644 migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md index f985a498..c2e6d82f 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md @@ -1,6 +1,6 @@ --- name: gcp-to-aws -description: "Migrate workloads from Google Cloud Platform to AWS — including AI and agentic workloads regardless of cloud provider. Triggers on: migrate from GCP, GCP to AWS, move off Google Cloud, migrate Terraform to AWS, migrate Cloud SQL to RDS, migrate GKE to EKS, migrate Cloud Run to Fargate, Google Cloud migration, migrate from OpenAI to Bedrock, move off OpenAI, switch from ChatGPT API to AWS, migrate from Gemini to Bedrock, migrate LangChain to Bedrock, migrate LangGraph to AWS, migrate agentic workloads to AWS, move AI workloads to AWS, migrate my AI app to AWS. Runs a 6-phase process: discover GCP resources from Terraform files, app code, or billing exports, clarify migration requirements, design AWS architecture, estimate costs, generate migration artifacts, and collect optional feedback. Clarify must finish before Design, Estimate, or Generate. Includes AI provider migration guidance (for example, OpenAI to Amazon Bedrock) by selecting closest-fit Bedrock model families for required modality, latency/quality targets, context windows, and cost constraints. Model mapping is compatibility-guided, not 1:1 parity; validate prompts, tool-calling behavior, and eval metrics before cutover. Do not use for: Azure or on-premises migrations to AWS, AWS-to-GCP reverse migration, general AWS architecture advice without migration intent, GCP-to-GCP refactoring, or multi-cloud deployments that do not involve migrating off GCP." +description: "Migrate workloads from Google Cloud Platform to AWS — including AI and agentic workloads regardless of cloud provider. Triggers on: migrate from GCP, GCP to AWS, move off Google Cloud, migrate Terraform to AWS, migrate Cloud SQL to RDS, migrate GKE to EKS, migrate Cloud Run to Fargate, migrate App Engine to Elastic Beanstalk, Google Cloud migration, migrate from OpenAI to Bedrock, move off OpenAI, switch from ChatGPT API to AWS, migrate from Gemini to Bedrock, migrate LangChain to Bedrock, migrate LangGraph to AWS, migrate agentic workloads to AWS, move AI workloads to AWS, migrate my AI app to AWS. Runs a 6-phase process: discover GCP resources from Terraform files, app code, or billing exports, clarify migration requirements, design AWS architecture, estimate costs, generate migration artifacts, and collect optional feedback. Clarify must finish before Design, Estimate, or Generate. Includes AI provider migration guidance (for example, OpenAI to Amazon Bedrock) by selecting closest-fit Bedrock model families for required modality, latency/quality targets, context windows, and cost constraints. Model mapping is compatibility-guided, not 1:1 parity; validate prompts, tool-calling behavior, and eval metrics before cutover. Do not use for: Azure or on-premises migrations to AWS, AWS-to-GCP reverse migration, general AWS architecture advice without migration intent, GCP-to-GCP refactoring, or multi-cloud deployments that do not involve migrating off GCP." --- # GCP-to-AWS Migration Skill @@ -39,6 +39,7 @@ Each phase loads reference files on demand. To keep per-turn context manageable | `design-refs/ai-openai-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"openai"` or `"both"` | | `design-refs/ai-anthropic-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"anthropic"` | | `design-refs/ai.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"other"` | +| `design-refs/elastic-beanstalk.md` | `google_app_engine_application` in inventory OR `compute_model == "managed_platform"` in preferences. Supplementary reference for EB configuration detail (platforms, IAM, VPC, deployment policies). Does not replace `compute.md` — both may be needed in mixed projects. | | `design-refs/design-ref-harness.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "harness"` | | `design-refs/design-ref-agentic-to-agentcore.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "strands"` | | `shared/retarget-gotchas.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "retarget"` | @@ -261,6 +262,7 @@ gcp-to-aws/ │ │ ├── index.md # Lookup table: GCP type → design-ref file │ │ ├── fast-path.md # Deterministic 1:1 mappings (Pass 1) │ │ ├── compute.md # Compute mappings (Cloud Run, GCE, GKE, etc.) +│ │ ├── elastic-beanstalk.md # Elastic Beanstalk (App Engine, managed platform) │ │ ├── database.md # Database mappings (Cloud SQL, Spanner, etc.) │ │ ├── storage.md # Storage mappings (GCS, Filestore, etc.) │ │ ├── networking.md # Networking mappings (VPC, LB, DNS, etc.) diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md index c59096af..bbc0668d 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md @@ -5,7 +5,8 @@ **Table lookup first:** Check `fast-path.md` **Direct Mappings** for this Terraform type. - `google_cloud_run_service`, `google_cloud_run_v2_service`, `google_cloudfunctions_function`, and `google_cloudfunctions2_function` are currently in Direct Mappings and usually resolve with `confidence: "deterministic"` when row conditions are met. -- `google_compute_instance`, `google_container_cluster`, and `google_app_engine_application` are not direct-mapped in `fast-path.md`; use the rubric below (typically `confidence: "inferred"`). +- `google_app_engine_application` is now in Direct Mappings (→ Elastic Beanstalk, confidence: `deterministic`). +- `google_compute_instance` and `google_container_cluster` are not direct-mapped in `fast-path.md`; use the rubric below (typically `confidence: "inferred"`). - If a resource is not eligible for Direct Mappings (or row conditions are not met), use the rubric below. ## Eliminators (Hard Blockers) @@ -17,15 +18,18 @@ | Cloud Functions | Lambda | Python version not supported (e.g., Python 2.7) → use custom runtime on Fargate | | GKE | EKS | Custom CRI incompatible → manual workaround or ECS | | Any | App Runner | **Closed to new customers (April 30 2026).** Do not target App Runner for new migrations. Use Fargate (default), Lambda (event-driven), or EKS (K8s required). | +| App Engine | Elastic Beanstalk | `compute_model: "container_orchestration"` or `"serverless"` in preferences → do not use EB, fall through to Fargate or Lambda | ## Signals (Decision Criteria) -### Cloud Run / App Engine +### Cloud Run - **Always-on** or **cold-start sensitive** → Fargate (not Lambda) - **Stateless microservice** + **<15 min execution** → Lambda - **HTTP-only** + **container-native** → Fargate preferred (better dev/prod parity) +Note: Cloud Run maps to Fargate via deterministic fast-path ("Always"). The `compute_model` preference does not affect Cloud Run mapping. + ### Cloud Functions - **Event-driven** + **<15 min** + **Python/Node/Go** → Lambda @@ -41,7 +45,16 @@ - **Kubernetes orchestration explicitly required** (`kubernetes = "eks-managed"` or `"eks-or-ecs"` in `preferences.json`) → EKS - **Default / no explicit K8s preference** (`kubernetes = "ecs-fargate"` or absent): - - → **Fargate** (absent kubernetes preference resolves to Fargate, not EKS — teams that want EKS answer A or B in Clarify) + - If `gcp-resource-inventory.json` contains `google_container_cluster` → EKS (IaC signal shows K8s workload) + - Otherwise → **Fargate** (no K8s signal; lower-ops default) + +### App Engine + +- **Default** → Elastic Beanstalk (PaaS-to-PaaS, preserves managed platform model) +- **User prefers container control** (`compute_model: "container_orchestration"`) → Fargate +- **Event-driven / scale-to-zero required** → Lambda + +After selecting Elastic Beanstalk, load `elastic-beanstalk.md` to populate `aws_config` (platform, deployment policy, IAM, VPC, sizing). ## 6-Criteria Rubric @@ -50,6 +63,7 @@ Apply in order; first match wins: 1. **Eliminators**: Does GCP config violate AWS constraints? If yes: switch to alternative 2. **Operational Model**: Managed (Lambda, Fargate) vs Self-Hosted (EC2, EKS)? - Prefer managed unless: Always-on + high baseline cost → EC2 + - For App Engine sources: Elastic Beanstalk (PaaS-to-PaaS) when `compute_model` is absent or `"managed_platform"` 3. **User Preference**: From `preferences.json`: `design_constraints.kubernetes`, `design_constraints.cost_sensitivity`? - If `kubernetes = "eks-managed"` → EKS (preserves K8s investment) - If `kubernetes = "eks-or-ecs"` → EKS with managed node groups (user is competent with K8s) @@ -102,6 +116,24 @@ Apply in order; first match wins: - → **AWS: EC2 t3.medium + Auto Scaling Group (min=0 in dev)** - Confidence: `inferred` +### Example 4a: App Engine (standard Python web app, default preference) + +- GCP: `google_app_engine_application` (runtime=python39, instance_class=F2) +- Signals: PaaS deployment, `compute_model` absent or `"managed_platform"` +- Fast-path condition met: `compute_model` not set to `"container_orchestration"` or `"serverless"` +- → **AWS: Elastic Beanstalk (Python 3.9, LoadBalanced, t3.small)** +- Confidence: `deterministic` (App Engine → EB direct mapping, condition met) + +### Example 4b: App Engine (user chose container orchestration) + +- GCP: `google_app_engine_application` (runtime=python39, instance_class=F2) +- Signals: PaaS deployment, but `compute_model: "container_orchestration"` in preferences +- Fast-path condition NOT met: falls through to rubric +- Criterion 1 (Eliminators): EB blocked (user chose container orchestration) +- Criterion 2 (Operational Model): Fargate (managed containers) +- → **AWS: Fargate (0.5 CPU, 1 GB memory)** +- Confidence: `inferred` (rubric-based override of default PaaS mapping) + ## Output Schema ```json diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md new file mode 100644 index 00000000..52045c72 --- /dev/null +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md @@ -0,0 +1,135 @@ +# Elastic Beanstalk Design Reference + +**Applies to:** Google App Engine (Standard/Flexible) + +## Key Distinction + +- **EB is application management** (AWS manages lifecycle: provisioning, load balancing, scaling, patching) vs **ECS/Fargate as infrastructure management** (user manages task definitions, service configs, scaling policies). +- "Don't want to manage servers" does **NOT** mean serverless/Lambda. Lambda imposes a different programming model: stateless functions, cold starts, event-driven invocation, 15-min max execution, no persistent connections. +- EB eliminates server management while preserving the standard application programming model: long-running processes, persistent connections, threads, local state, WebSockets. + +## When to Use + +Routing signals — these apply ONLY when `google_app_engine_application` is in the inventory (any match is sufficient): + +- `google_app_engine_application` detected in Terraform (strongest PaaS-to-PaaS signal) +- User answers `compute_model: "managed_platform"` in Clarify (Q7b) +- User explicitly requests "managed platform" or "Elastic Beanstalk" for their App Engine workloads + +These signals do NOT apply to Cloud Run resources. Cloud Run maps to Fargate unconditionally via fast-path. + +## NOT the Right Choice + +- User explicitly wants serverless/Lambda (event-driven functions, stateless, cold starts) → use Lambda +- User wants fine-grained container orchestration control → use ECS/Fargate or EKS +- User requires scale-to-zero → use Lambda or Fargate with scaling policies +- GPU workloads → use EC2 or EKS +- Kubernetes orchestration required → use EKS + +## Supported Platforms + +| Platform | Versions | Base OS | Notes | +| ---------- | ----------------- | ------- | ---------------------------------------- | +| Python | 3.9, 3.10, 3.11, 3.12 | AL2023 | | +| Node.js | 18, 20, 22 | AL2023 | | +| Java Corretto | 11, 17, 21 | AL2023 | .jar (standalone) vs .war (Tomcat) | +| .NET | 6, 8, 10 | AL2023 | Requires pre-built artifacts | +| Go | 1.21+ | AL2023 | | +| Ruby | 3.2, 3.3 | AL2023 | | +| Docker | Single/Multi-container | AL2023 | | +| PHP | 8.1, 8.2, 8.3 | AL2023 | | + +## Platform Detection Rules + +Detect from app source to select EB platform automatically: + +| File/Pattern | Platform | +| ----------------------------------------- | -------------- | +| `requirements.txt` or `Pipfile` | Python | +| `package.json` | Node.js | +| `pom.xml` or `build.gradle` with .jar output | Java Corretto | +| `pom.xml` or `build.gradle` with .war output | Java Tomcat | +| `*.csproj` or `*.sln` | .NET | +| `go.mod` | Go | +| `Gemfile` | Ruby | +| `Dockerfile` | Docker | +| `composer.json` | PHP | + +## Environment Types + +- **Web server** (default): Handles HTTP requests via ALB. Use for APIs, web apps, frontends. +- **Worker**: Processes background jobs from SQS queue. No public endpoint. Use for async tasks, cron-like jobs, batch processing. + +## Configuration + +**IaC tool:** AWS CLI (`aws elasticbeanstalk` commands). Do not use EB CLI (avoids install dependency on target machine). + +**Port:** All AL2023 platforms default to port 5000. + +**Deployment policies:** + +| Policy | Use case | +| --------------------------- | --------------------------------- | +| AllAtOnce | Dev/test (fastest, brief downtime)| +| Rolling | Prod with some tolerance | +| RollingWithAdditionalBatch | Prod (maintains full capacity) | +| Immutable | Prod (safe rollback) | +| TrafficSplitting | Canary/prod (percentage-based) | + +**Secrets:** Use `environmentsecrets` namespace to pull from Secrets Manager or SSM Parameter Store. Requires platform versions March 2025+. + +**IAM:** + +- Instance profile: scan source for AWS SDK usage; attach least-privilege policies for accessed services +- Service role: `aws-elasticbeanstalk-service-role` (auto-created on first environment) + +**VPC:** + +- Web tier: public subnets with ALB +- Worker tier: private subnets (no public access needed) + +**Scaling:** + +- Configure min/max instances +- Scaling triggers: CPU utilization, network out, latency, request count + +## GCP App Engine to EB Mapping + +| App Engine Feature | EB Equivalent | +| ------------------------------- | ------------------------------------------ | +| App Engine Standard | EB with matching platform (Python, Node, etc.) | +| App Engine Flexible | EB Docker platform | +| `app.yaml` env vars | EB environment properties | +| `cron.yaml` | EB worker + EventBridge scheduled events | +| Task queues | EB worker + SQS | +| `instance_class` (F1/F2/F4) | Instance type selection (t3.small/medium) | +| Automatic scaling min/max | EB auto-scaling min/max instances | + +## Sizing Defaults + +| Environment | Type | Instance | ALB | Min Instances | Multi-AZ | +| ----------- | --------------- | ----------- | --- | ------------- | -------- | +| Dev | SingleInstance | t3.small | No | 1 | No | +| Prod | LoadBalanced | t3.medium+ | Yes | 2 | Yes | + +## Output Schema + +```json +{ + "gcp_type": "google_app_engine_application", + "gcp_address": "example-app", + "gcp_config": { + "runtime": "python39", + "instance_class": "F2" + }, + "aws_service": "Elastic Beanstalk", + "aws_config": { + "platform": "Python 3.9", + "environment_type": "LoadBalanced", + "instance_type": "t3.small", + "region": "us-east-1" + }, + "confidence": "deterministic", + "rationale": "Direct Mapping: google_app_engine_application → Elastic Beanstalk (compute_model absent or managed_platform)" +} +``` diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md index ccadd150..cf764390 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md @@ -51,6 +51,7 @@ JSON artifacts **must** keep the `confidence` string values above. When speaking | `google_secret_manager_secret` | Secrets Manager | Always | Create secret metadata and IAM-scoped access | | `google_secret_manager_secret_version` | Secrets Manager | Always | Carry current value or explicit migration TODO | | `google_redis_instance` | ElastiCache Redis | Always | 1:1 mapping; preserve cluster mode and node type | +| `google_app_engine_application` | Elastic Beanstalk | `compute_model` absent or `"managed_platform"` | PaaS-to-PaaS; detect runtime from app.yaml. If `compute_model: "container_orchestration"` or `"serverless"`, use rubric in `compute.md` (confidence: `inferred`). | ### Cloud SQL PostgreSQL / MySQL — NOT in Direct Mappings @@ -79,6 +80,8 @@ These GCP resources do **not** require AWS equivalents in v1.0: | `google_compute_address` | Elastic IPs managed by ALB/NAT (not standalone) | | `google_identity_platform_*` | Auth provider — keep existing solution, do not migrate to AWS Cognito or any AWS auth | | `google_firebase_auth_*` | Auth provider — keep existing solution, do not migrate to AWS Cognito or any AWS auth | +| `google_app_engine_standard_app_version` | Configuration of parent `google_app_engine_application`; no separate AWS resource | +| `google_app_engine_flexible_app_version` | Configuration of parent `google_app_engine_application`; no separate AWS resource | ## Preferred AWS Target Services @@ -87,10 +90,13 @@ For the workload categories below, always steer toward the **preferred** AWS ser | Workload Category | Preferred AWS Target | Rationale | | ----------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | | Containerized workloads | Fargate (default), Lambda (event-driven), EKS (K8s required) | Deeper integration with VPC, ALB, IAM, and auto-scaling than lighter-weight alternatives | +| PaaS / managed platform | Elastic Beanstalk (when `compute_model: "managed_platform"` or App Engine source) | Preserves managed platform model; AWS manages deployments, scaling, patching, monitoring | | Third-party auth in use | Keep existing auth provider | Startups using Auth0, Firebase Auth, Clerk, etc. should preserve their existing investment | **Enforcement:** Check every resource mapping against this table before writing to `aws-design.json` or `aws-design-billing.json`. If a mapping would select a non-preferred service, substitute the preferred alternative. +**Precedence:** Match resources to the **first applicable row**. Cloud Run matches "Containerized workloads" regardless of `compute_model`. The "PaaS / managed platform" row applies only to App Engine resources and resources that reached Pass 2 without matching a prior row. + **Exception:** For Cloud SQL PostgreSQL/MySQL, **Q6 availability always overrides** any implicit Aurora preference. Do not substitute Aurora when `availability` is `single-az` or `multi-az`. ## Secondary Behavior Lookups diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/index.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/index.md index 71ff005c..c94ef9b4 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/index.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/index.md @@ -10,7 +10,7 @@ | Cloud Functions | `google_cloudfunctions_function` | `compute.md` | Lambda | | Compute Engine (VM) | `google_compute_instance` | `compute.md` | EC2 or Fargate | | GKE | `google_container_cluster` | `compute.md` | EKS | -| App Engine | `google_app_engine_application` | `compute.md` | Fargate or Amplify | +| App Engine | `google_app_engine_application` | `compute.md` | Elastic Beanstalk (default) or Fargate | ## Database Services diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify-compute.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify-compute.md index 69b0a4dc..634835ca 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify-compute.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify-compute.md @@ -31,7 +31,42 @@ Record Category B answers in `metadata.inventory_clarifications`. ## Category C — Compute Model (If Compute Resources Present) -_Fire when:_ Compute resources present (Cloud Run, Cloud Functions, GKE, GCE). +_Fire when:_ Compute resources present (Cloud Run, Cloud Functions, GKE, GCE, App Engine). + +--- + +## Q7b — What compute operational model do you prefer for your App Engine workloads? + +_Fire when:_ App Engine present in inventory (`google_app_engine_application`). Skip when: no App Engine in inventory. + +**Rationale:** GCP App Engine is a PaaS that can map to different AWS compute targets depending on whether the user wants to preserve the managed platform model (Elastic Beanstalk), switch to direct container control (Fargate/ECS), or go serverless (Lambda). This drives the fundamental routing decision for App Engine resources. + +Note: This question does NOT affect Cloud Run resources. Cloud Run maps to Fargate via its own deterministic fast-path regardless of this answer. + +> Your App Engine setup uses a managed platform (you provide code, Google manages everything else). On AWS, you have a few options for these workloads: +> +> A) Managed platform — I provide code, AWS manages everything else (like App Engine today) +> B) Container orchestration — I want direct control over containers and scaling +> C) Serverless — Event-driven functions, scale-to-zero, stateless +> D) I don't know — recommend the best fit + +| Answer | Recommendation Impact | +| ------------------- | ----------------------------------------------------------------------------------------- | +| Managed platform | Elastic Beanstalk — preserves PaaS model, AWS manages deployments/scaling/patching | +| Container control | ECS Fargate — direct container management with full VPC/ALB/IAM integration | +| Serverless | Lambda — event-driven, stateless functions with scale-to-zero | +| I don't know | Default: Elastic Beanstalk (PaaS-to-PaaS, closest match to App Engine) | + +Interpret: + +``` +A -> compute_model: "managed_platform" — Elastic Beanstalk recommended +B -> compute_model: "container_orchestration" — ECS Fargate recommended +C -> compute_model: "serverless" — Lambda recommended +D -> same as default (A) +``` + +**Default:** **A** (`compute_model: "managed_platform"`). App Engine is PaaS; Elastic Beanstalk is the closest AWS equivalent. Users who skip or say "I don't know" get the PaaS-to-PaaS path. --- diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify.md index e78289bd..6b0b4fa9 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify.md @@ -315,7 +315,7 @@ When user confirms: mark all rows `"confirmed": true` in `metadata.detected_sett | -------- | ------------------ | ------------------------------------------------------------------------------ | --------------------- | ------------------------------------------------------------------------------------------------------------------- | | **A** | Global/Strategic | **Always fires** | `clarify-global.md` | Q1 (location), Q2 (compliance), Q3 (GCP spend), Q4 (funding stage), Q5 (multi-cloud), Q6 (uptime), Q7 (maintenance) | | **B** | Configuration Gaps | `billing-profile.json` exists AND `gcp-resource-inventory.json` does NOT exist | `clarify-compute.md` | Cloud SQL HA, Cloud Run count, Memorystore memory, Functions gen | -| **C** | Compute Model | Compute resources present (Cloud Run, Cloud Functions, GKE, GCE) | `clarify-compute.md` | Q8 (K8s sentiment), Q9 (WebSocket), Q10 (Cloud Run traffic), Q11 (Cloud Run spend) | +| **C** | Compute Model | Compute resources present (Cloud Run, Cloud Functions, GKE, GCE, App Engine) | `clarify-compute.md` | Q7b (compute model), Q8 (K8s sentiment), Q9 (WebSocket), Q10 (Cloud Run traffic), Q11 (Cloud Run spend) | | **D** | Database Model | Database resources present (Cloud SQL, Spanner, Memorystore) | `clarify-database.md` | Q12 (DB traffic pattern), Q13 (DB I/O), Q13b (DB size) | | **E** | Migration Posture | **Disabled by default** — requires explicit user opt-in | _(inline below)_ | HA upgrades, right-sizing | | **F** | AI/Bedrock | `ai-workload-profile.json` exists | `clarify-ai.md` | Q14–Q26 (Q14–Q22 always; Q23–Q26 only when `agentic_profile.is_agentic == true`) | @@ -324,7 +324,7 @@ When user confirms: mark all rows `"confirmed": true` in `metadata.detected_sett 1. Category A is always active. 2. Check for billing-only mode — if `billing-profile.json` exists and `gcp-resource-inventory.json` does NOT, Category B is active. -3. Check for compute resources — if present, Category C is active. Within C, skip Q8 if no GKE present. Skip Q10/Q11 if no Cloud Run present. +3. Check for compute resources — if present, Category C is active. Within C, skip Q7b if no App Engine present. Skip Q8 if no GKE present. Skip Q10/Q11 if no Cloud Run present. 4. Check for database resources — if present, Category D is active. 5. Category E is disabled by default. Offered after the last batch completes in Step 4 (see **Category E Opt-In** in Step 4). If user declines or does not respond, apply Category E defaults (no HA upgrades, no right-sizing). 6. Check for `ai-workload-profile.json` — if present, Category F is active. @@ -373,7 +373,7 @@ After determining active categories, organize questions into **up to three batch | Batch | Name | Categories | Questions | Fires When | | ----- | ---------------------- | ------------------------------------------ | --------------------------------- | ----------------------------------------- | | **1** | Strategic Requirements | A (Global/Strategic) | Q1–Q7 (minus Q4) | Always | -| **2** | Infrastructure | B (Config Gaps), C (Compute), D (Database) | Q8–Q13b + Category B prompts | Any compute or database resources present | +| **2** | Infrastructure | B (Config Gaps), C (Compute), D (Database) | Q7b, Q8–Q13b + Category B prompts | Any compute or database resources present | | **3** | AI Workloads | F (AI/Bedrock) | Q14–Q26 (Q23–Q26 only if agentic) | `ai-workload-profile.json` exists | **Determine active batches:** @@ -537,6 +537,8 @@ If user opts in, present Q-E1–Q-E2 (defined in **Category E — Migration Post | ---------------------------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | | Early-stage funding path | Q3 = lower spend band | Entry-tier migration funding program review | | Growth-stage funding path | Q3 = higher spend band | Migration funding/support program review based on spend profile | +| Managed platform preference | Q7b = A (managed platform) | Elastic Beanstalk for App Engine compute targets | +| Container orchestration pref | Q7b = B (container orchestration) | ECS Fargate for App Engine targets (overrides default EB mapping) | | Must stay portable | Q5 = Yes multi-cloud | EKS only, no ECS Fargate | | Kubernetes-averse | Q5 = No + Q8 = Frustrated | ECS Fargate strongly recommended | | WebSocket app | Q9 = Yes | ALB WebSocket config required | @@ -623,6 +625,7 @@ Assemble all interpreted answers from the completed batches into the final `$MIG "funding_stage": { "value": "series-a", "chosen_by": "user" }, "availability": { "value": "multi-az", "chosen_by": "default" }, "cutover_strategy": { "value": "maintenance-window-weekly", "chosen_by": "user" }, + "compute_model": { "value": "managed_platform", "chosen_by": "user" }, "kubernetes": { "value": "eks-or-ecs", "chosen_by": "user" }, "database_traffic": { "value": "steady", "chosen_by": "user" }, "db_io_workload": { "value": "medium", "chosen_by": "user" }, @@ -676,6 +679,7 @@ After writing `preferences.json`, delete `$MIGRATION_DIR/preferences-draft.json` | Q5 — Multi-cloud | B (AWS-only) | no constraint | | Q6 — Uptime | B (significant) | `availability: "multi-az"` | | Q7 — Maintenance | D (flexible) | `cutover_strategy: "flexible"` | +| Q7b — Compute model | A (managed platform) | `compute_model: "managed_platform"` (App Engine only; skipped if no App Engine) | | Q8 — K8s sentiment | B (neutral) | `kubernetes: "eks-or-ecs"` | | Q9 — WebSocket | B (no) | no constraint | | Q10 — Cloud Run traffic | C (24/7) | `cloud_run_traffic_pattern: "constant-24-7"` | diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md index 9625de25..4139484b 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md @@ -24,9 +24,12 @@ For each PRIMARY resource in the cluster: 1. Extract GCP type (e.g., `google_sql_database_instance`) 2. Look up in `design-refs/fast-path.md` → **Direct Mappings** table (not the Preferred Target table — that applies later in Pass 2). -3. If found and conditions match: assign AWS service with confidence = **`deterministic`**. Set `human_expertise_required: false` (no Direct Mapping row requires it). -4. If `gcp_type` is `google_sql_database_instance` with PostgreSQL or MySQL engine: **always proceed to Pass 2** (Cloud SQL is not in Direct Mappings — see `fast-path.md`). Confidence = **`inferred`** after rubric. -5. If not found: proceed to Pass 2 (confidence will be **`inferred`** after rubric, or **`billing_inferred`** on the billing-only path). +3. If found, evaluate the row's **Conditions** column: + - `Always` → conditions met. + - Conditional rows (e.g., `google_app_engine_application` requires `compute_model` absent or `"managed_platform"`): read `preferences.json → design_constraints.compute_model.value`. If the condition is NOT met (e.g., user chose `"container_orchestration"` or `"serverless"`), treat as **not found** — proceed to Pass 2. +4. If found and conditions match: assign AWS service with confidence = **`deterministic`**. Set `human_expertise_required: false` (no Direct Mapping row requires it). +5. If `gcp_type` is `google_sql_database_instance` with PostgreSQL or MySQL engine: **always proceed to Pass 2** (Cloud SQL is not in Direct Mappings — see `fast-path.md`). Confidence = **`inferred`** after rubric. +6. If not found (or conditions not met): proceed to Pass 2 (confidence will be **`inferred`** after rubric, or **`billing_inferred`** on the billing-only path). **Definitions:** See the top of `design-refs/fast-path.md` for **`deterministic` vs `inferred` vs `billing_inferred`** and the note that **index.md “Typical AWS target” ≠ deterministic**. diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design.md index b3c73975..7defce94 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design.md @@ -86,6 +86,7 @@ Sub-design files may reference rubrics in `design-refs/`: - `design-refs/index.md` — GCP type → rubric file lookup - `design-refs/fast-path.md` — Direct (table) mappings vs rubric path; **User-facing vocabulary** for presenting `confidence` to users (**Standard pairing** / **Tailored to your setup** / **Estimated from billing only**) - `design-refs/compute.md` — Compute service rubric +- `design-refs/elastic-beanstalk.md` — Elastic Beanstalk reference (App Engine, managed platform) - `design-refs/database.md` — Database service rubric - `design-refs/storage.md` — Storage service rubric - `design-refs/networking.md` — Networking service rubric diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/shared/pricing-cache.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/shared/pricing-cache.md index d8ab22c8..754de6ca 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/shared/pricing-cache.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/shared/pricing-cache.md @@ -30,6 +30,21 @@ Linux/x86, on-demand. | Per GB-second (over 6B) | $0.000015 | | Free tier | 1M requests + 400K GB-sec/month | +### Elastic Beanstalk + +| Metric | Rate | +| ------ | ---- | +| Service fee | $0.00 (free — no additional charge for EB itself) | + +Costs are the underlying resources (EC2, ALB, EBS, CloudWatch). Typical estimates: + +| Profile | Underlying Resources | Estimated Monthly | +| ------- | -------------------- | ----------------- | +| Dev (single instance, t3.small, no ALB) | EC2 t3.small | ~$15 | +| Dev + RDS (t3.small + db.t4g.micro) | EC2 + Aurora Serverless | ~$30–50 | +| Prod (load-balanced, 2× t3.medium, ALB) | 2× EC2 + ALB + EBS | ~$95–120 | +| Prod + RDS (2× t3.medium, ALB, db.t4g.medium) | EC2 + ALB + Aurora | ~$175–220 | + ### EKS | Metric | Rate | From c9dd4f17e6cbb0f000c6fdeecaeb746c9f032bc3 Mon Sep 17 00:00:00 2001 From: Paul Pollack Date: Wed, 1 Jul 2026 14:06:01 -0400 Subject: [PATCH 2/8] fix(gcp-to-aws): correct examples and output schema in compute.md Fix pre-existing inaccuracies where Cloud Run and Cloud Functions examples showed rubric-based evaluation (inferred) when they actually use deterministic fast-path mappings. Replace the single Output Schema with two examples showing both deterministic and inferred patterns. Also add compute_model to the User Preference criterion description in design-infra.md. --- .../references/design-refs/compute.md | 53 +++++++++++++------ .../references/phases/design/design-infra.md | 2 +- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md index bbc0668d..1e58be3f 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md @@ -82,29 +82,26 @@ Apply in order; first match wins: ### Example 1: Cloud Run (stateless API) - GCP: `google_cloud_run_service` (memory=512MB, timeout=60s, min_instances=1) -- Signals: HTTP, stateless, always-on -- Criterion 1 (Eliminators): PASS (60s < 15min doesn't apply; stateless OK) -- Criterion 2 (Operational Model): FARGATE preferred +- Fast-path: `google_cloud_run_service` → Fargate (Always, condition met) - → **AWS: Fargate (0.5 CPU, 1 GB memory)** -- Confidence: `inferred` (rubric-based — Cloud Run is not in fast-path) +- Confidence: `deterministic` (Direct Mapping, no rubric needed) ### Example 2a: Cloud Functions (event processor, short-running) - GCP: `google_cloudfunctions_function` (runtime=python39, timeout=540s) -- Signals: Event-driven, 540s = 9 minutes (< 15min limit) -- Criterion 1 (Eliminators): PASS on timeout (540s < 900s) -- Criterion 2 (Operational Model): Lambda preferred for event-driven + short-running +- Fast-path: `google_cloudfunctions_function` → Lambda (Always, condition met) - → **AWS: Lambda with EventBridge trigger** -- Confidence: `inferred` +- Confidence: `deterministic` (Direct Mapping, no rubric needed) -### Example 2b: Cloud Functions (long-running batch processor) +### Example 2b: Cloud Functions (long-running, timeout exceeds Lambda limit) - GCP: `google_cloudfunctions_function` (runtime=python39, timeout=1200s) -- Signals: Event-driven but 1200s = 20 minutes (> 15min limit) -- Criterion 1 (Eliminators): FAIL on timeout (1200s > 900s) → **cannot use Lambda** +- Fast-path: `google_cloudfunctions_function` → Lambda (Always) +- However, Eliminator fires: timeout 1200s > Lambda max 900s → **cannot use Lambda** +- Eliminator overrides fast-path → falls through to rubric - Criterion 2 (Operational Model): Fargate (managed + can handle longer execution) - → **AWS: Fargate (0.5 CPU, 1 GB memory) with EventBridge trigger** -- Confidence: `inferred` +- Confidence: `inferred` (eliminator forced rubric fallback) ### Example 3: Compute Engine (background job) @@ -136,6 +133,8 @@ Apply in order; first match wins: ## Output Schema +**Deterministic (fast-path) example:** + ```json { "gcp_type": "google_cloud_run_service", @@ -150,15 +149,35 @@ Apply in order; first match wins: "memory_mb": 1024, "region": "us-east-1" }, + "confidence": "deterministic", + "rationale": "Direct Mapping: google_cloud_run_service → Fargate (Always)" +} +``` + +**Inferred (rubric-based) example:** + +```json +{ + "gcp_type": "google_compute_instance", + "gcp_address": "batch-worker", + "gcp_config": { + "machine_type": "e2-medium", + "region": "us-central1" + }, + "aws_service": "EC2", + "aws_config": { + "instance_type": "t3.medium", + "region": "us-east-1" + }, "confidence": "inferred", - "rationale": "Rubric: Cloud Run (stateless, <15min) → Fargate (always-on, managed)", + "rationale": "Rubric: Compute Engine (always-on batch job) → EC2 with Auto Scaling", "rubric_applied": [ "Eliminators: PASS", - "Operational Model: Managed preferred", - "User Preference: N/A", + "Operational Model: EC2 (explicit compute control)", + "User Preference: cost_sensitivity → Auto Scaling", "Feature Parity: Full", - "Cluster Context: Fargate affinity", - "Simplicity: Fargate (1 service)" + "Cluster Context: N/A", + "Simplicity: EC2 + ASG" ] } ``` diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md index 4139484b..ce580e61 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md @@ -61,7 +61,7 @@ For resources not covered by fast-path: 6. Evaluate 6 criteria (1-sentence each): - **Eliminators**: Feature incompatibility (hard blocker) - **Operational Model**: Managed vs self-hosted fit - - **User Preference**: From `preferences.json` design_constraints + - **User Preference**: From `preferences.json` design_constraints (includes `compute_model`, `kubernetes`, `cost_sensitivity`) - **Feature Parity**: GCP feature → AWS feature availability - **Cluster Context**: Affinity with other resources in this cluster - **Simplicity**: Prefer fewer resources / less config From 31ef3230152a70f7534315de92876c112d31d101 Mon Sep 17 00:00:00 2001 From: Paul Pollack Date: Mon, 13 Jul 2026 15:29:33 -0400 Subject: [PATCH 3/8] fix(gcp-to-aws): address EB review feedback (PR #101) - elastic-beanstalk.md: drop stale version matrix, keep platform names + link to EB supported-platforms docs (versions rot quickly) - reconcile Tomcat as a separate platform from Java SE (Corretto) - correct port guidance: AL2023 proxies to 5000/PORT by default; app must bind the advertised port or first-deploy health checks fail - verify secrets namespace (aws:elasticbeanstalk:application:environmentsecrets) and pin platform cutoff to 2025-03-26 - VPC: ALB in public subnets, instances in private subnets - make sizing consistent (LoadBalanced -> t3.medium+) in EB output schema and compute.md Example 4a - treat google_app_engine_*_app_version as config sources (runtime/ instance_class/scaling), not skips; one EB environment per App Engine service so multi-service apps don't collapse - pricing-cache.md: label EB estimate rows as RDS db.t4g.* (not Aurora) --- .../skills/gcp-to-aws/SKILL.md | 18 +-- .../references/design-refs/compute.md | 23 ++-- .../design-refs/elastic-beanstalk.md | 129 +++++++++++------- .../references/design-refs/fast-path.md | 46 +++---- .../references/design-refs/index.md | 12 +- .../phases/clarify/clarify-compute.md | 12 +- .../references/phases/clarify/clarify.md | 52 +++---- .../references/shared/pricing-cache.md | 16 +-- 8 files changed, 169 insertions(+), 139 deletions(-) diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md index c2e6d82f..8a2a7811 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md @@ -33,16 +33,16 @@ Each phase loads reference files on demand. To keep per-turn context manageable **Conditional reference files (load ONLY when condition is true):** -| File | Condition | -| ------------------------------------------------ | -------------------------------------------------------------------------------------------------- | -| `design-refs/ai-gemini-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"gemini"` or `"both"` | -| `design-refs/ai-openai-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"openai"` or `"both"` | -| `design-refs/ai-anthropic-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"anthropic"` | -| `design-refs/ai.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"other"` | +| File | Condition | +| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `design-refs/ai-gemini-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"gemini"` or `"both"` | +| `design-refs/ai-openai-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"openai"` or `"both"` | +| `design-refs/ai-anthropic-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"anthropic"` | +| `design-refs/ai.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"other"` | | `design-refs/elastic-beanstalk.md` | `google_app_engine_application` in inventory OR `compute_model == "managed_platform"` in preferences. Supplementary reference for EB configuration detail (platforms, IAM, VPC, deployment policies). Does not replace `compute.md` — both may be needed in mixed projects. | -| `design-refs/design-ref-harness.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "harness"` | -| `design-refs/design-ref-agentic-to-agentcore.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "strands"` | -| `shared/retarget-gotchas.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "retarget"` | +| `design-refs/design-ref-harness.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "harness"` | +| `design-refs/design-ref-agentic-to-agentcore.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "strands"` | +| `shared/retarget-gotchas.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "retarget"` | When adding new reference files, verify the phase's total loaded instructions remain under budget. If a new file would exceed ~800 lines when combined with other loaded refs, split it or make it conditional. diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md index 1e58be3f..98793ec7 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md @@ -11,14 +11,14 @@ ## Eliminators (Hard Blockers) -| GCP Service | AWS | Blocker | -| --------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Cloud Run | Lambda | Execution time >15 min → use Fargate | -| Cloud Run | Fargate | GPU workload or >16 vCPU or >120 GB memory → use EC2 | -| Cloud Functions | Lambda | Python version not supported (e.g., Python 2.7) → use custom runtime on Fargate | -| GKE | EKS | Custom CRI incompatible → manual workaround or ECS | -| Any | App Runner | **Closed to new customers (April 30 2026).** Do not target App Runner for new migrations. Use Fargate (default), Lambda (event-driven), or EKS (K8s required). | -| App Engine | Elastic Beanstalk | `compute_model: "container_orchestration"` or `"serverless"` in preferences → do not use EB, fall through to Fargate or Lambda | +| GCP Service | AWS | Blocker | +| --------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Cloud Run | Lambda | Execution time >15 min → use Fargate | +| Cloud Run | Fargate | GPU workload or >16 vCPU or >120 GB memory → use EC2 | +| Cloud Functions | Lambda | Python version not supported (e.g., Python 2.7) → use custom runtime on Fargate | +| GKE | EKS | Custom CRI incompatible → manual workaround or ECS | +| Any | App Runner | **Closed to new customers (April 30 2026).** Do not target App Runner for new migrations. Use Fargate (default), Lambda (event-driven), or EKS (K8s required). | +| App Engine | Elastic Beanstalk | `compute_model: "container_orchestration"` or `"serverless"` in preferences → do not use EB, fall through to Fargate or Lambda | ## Signals (Decision Criteria) @@ -115,15 +115,16 @@ Apply in order; first match wins: ### Example 4a: App Engine (standard Python web app, default preference) -- GCP: `google_app_engine_application` (runtime=python39, instance_class=F2) +- GCP: `google_app_engine_standard_app_version` (service=default, runtime=python39, instance_class=F2) under `google_app_engine_application` +- Note: `runtime`/`instance_class` come from the `*_app_version` resource, not the parent `google_app_engine_application`. Map one EB environment per App Engine service. - Signals: PaaS deployment, `compute_model` absent or `"managed_platform"` - Fast-path condition met: `compute_model` not set to `"container_orchestration"` or `"serverless"` -- → **AWS: Elastic Beanstalk (Python 3.9, LoadBalanced, t3.small)** +- → **AWS: Elastic Beanstalk (Python 3.9, LoadBalanced, t3.medium)** — LoadBalanced uses t3.medium+ per `elastic-beanstalk.md` Sizing Defaults - Confidence: `deterministic` (App Engine → EB direct mapping, condition met) ### Example 4b: App Engine (user chose container orchestration) -- GCP: `google_app_engine_application` (runtime=python39, instance_class=F2) +- GCP: `google_app_engine_standard_app_version` (service=default, runtime=python39, instance_class=F2) under `google_app_engine_application` - Signals: PaaS deployment, but `compute_model: "container_orchestration"` in preferences - Fast-path condition NOT met: falls through to rubric - Criterion 1 (Eliminators): EB blocked (user chose container orchestration) diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md index 52045c72..d40c5182 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md @@ -28,32 +28,37 @@ These signals do NOT apply to Cloud Run resources. Cloud Run maps to Fargate unc ## Supported Platforms -| Platform | Versions | Base OS | Notes | -| ---------- | ----------------- | ------- | ---------------------------------------- | -| Python | 3.9, 3.10, 3.11, 3.12 | AL2023 | | -| Node.js | 18, 20, 22 | AL2023 | | -| Java Corretto | 11, 17, 21 | AL2023 | .jar (standalone) vs .war (Tomcat) | -| .NET | 6, 8, 10 | AL2023 | Requires pre-built artifacts | -| Go | 1.21+ | AL2023 | | -| Ruby | 3.2, 3.3 | AL2023 | | -| Docker | Single/Multi-container | AL2023 | | -| PHP | 8.1, 8.2, 8.3 | AL2023 | | +Detect the **platform name** (below), then resolve the exact platform version at generate time. **Do not hardcode language version numbers** — the EB platform lineup changes frequently (versions are added and retired). Look up the current version against the [EB supported platforms documentation](https://docs.aws.amazon.com/elasticbeanstalk/latest/platforms/platforms-supported.html) or `aws elasticbeanstalk list-available-solution-stacks`, and pick the newest platform version that supports the app's language version. + +| Platform | Base OS | Notes | +| ------------------ | ------- | --------------------------------------------------------------------------- | +| Python | AL2023 | | +| Node.js | AL2023 | | +| Java SE (Corretto) | AL2023 | Standalone `.jar` applications | +| Tomcat | AL2023 | **Separate platform** from Java SE; for `.war` web apps (Corretto + Tomcat) | +| .NET Core on Linux | AL2023 | Requires pre-built artifacts | +| Go | AL2023 | | +| Ruby | AL2023 | | +| PHP | AL2023 | | +| Docker | AL2023 | Single container, or ECS-managed multi-container | + +All current platforms run on Amazon Linux 2023 (AL2 branches are being retired). ## Platform Detection Rules Detect from app source to select EB platform automatically: -| File/Pattern | Platform | -| ----------------------------------------- | -------------- | -| `requirements.txt` or `Pipfile` | Python | -| `package.json` | Node.js | -| `pom.xml` or `build.gradle` with .jar output | Java Corretto | -| `pom.xml` or `build.gradle` with .war output | Java Tomcat | -| `*.csproj` or `*.sln` | .NET | -| `go.mod` | Go | -| `Gemfile` | Ruby | -| `Dockerfile` | Docker | -| `composer.json` | PHP | +| File/Pattern | Platform | +| -------------------------------------------- | ------------------ | +| `requirements.txt` or `Pipfile` | Python | +| `package.json` | Node.js | +| `pom.xml` or `build.gradle` with .jar output | Java SE (Corretto) | +| `pom.xml` or `build.gradle` with .war output | Tomcat | +| `*.csproj` or `*.sln` | .NET Core on Linux | +| `go.mod` | Go | +| `Gemfile` | Ruby | +| `Dockerfile` | Docker | +| `composer.json` | PHP | ## Environment Types @@ -64,19 +69,24 @@ Detect from app source to select EB platform automatically: **IaC tool:** AWS CLI (`aws elasticbeanstalk` commands). Do not use EB CLI (avoids install dependency on target machine). -**Port:** All AL2023 platforms default to port 5000. +**Port:** On AL2023 platforms the reverse proxy (nginx) forwards to the application on port **5000** by default, and EB sets the `PORT` environment variable to that value. However, most application frameworks bind to their own default port unless told otherwise (e.g. Node/Express often 3000, Python/gunicorn 8000, .NET/Kestrel 5000/8080). **The app must listen on the port EB advertises**, or the first deploy fails its health checks. Two safe options: + +- **Have the app read `PORT`** and bind to it (e.g. `process.env.PORT`, `os.environ["PORT"]`) — recommended, and how App Engine apps already behave. +- **Set the `PORT` environment property** (namespace `aws:elasticbeanstalk:application:environment`) to whatever fixed port the app listens on. + +Migrated App Engine apps already read `PORT` (App Engine sets it too), so the first option usually works with no code change. **Deployment policies:** -| Policy | Use case | -| --------------------------- | --------------------------------- | -| AllAtOnce | Dev/test (fastest, brief downtime)| -| Rolling | Prod with some tolerance | -| RollingWithAdditionalBatch | Prod (maintains full capacity) | -| Immutable | Prod (safe rollback) | -| TrafficSplitting | Canary/prod (percentage-based) | +| Policy | Use case | +| -------------------------- | ---------------------------------- | +| AllAtOnce | Dev/test (fastest, brief downtime) | +| Rolling | Prod with some tolerance | +| RollingWithAdditionalBatch | Prod (maintains full capacity) | +| Immutable | Prod (safe rollback) | +| TrafficSplitting | Canary/prod (percentage-based) | -**Secrets:** Use `environmentsecrets` namespace to pull from Secrets Manager or SSM Parameter Store. Requires platform versions March 2025+. +**Secrets:** Use the `aws:elasticbeanstalk:application:environmentsecrets` namespace to fetch values from Secrets Manager or SSM Parameter Store into environment variables at instance bootstrap. Supported on platform versions released **on or after March 26, 2025**. (JSON-key extraction from a Secrets Manager secret — appending `:keyName` to the ARN — requires platform versions on or after January 13, 2026.) Values are pulled at bootstrap only; rotate via `UpdateEnvironment` or `RestartAppServer`. **IAM:** @@ -85,8 +95,8 @@ Detect from app source to select EB platform automatically: **VPC:** -- Web tier: public subnets with ALB -- Worker tier: private subnets (no public access needed) +- Web tier: ALB in public subnets, EC2 instances in private subnets (instances reach the internet via NAT for outbound; only the ALB is public-facing) +- Worker tier: instances in private subnets (no public access needed) **Scaling:** @@ -95,41 +105,60 @@ Detect from app source to select EB platform automatically: ## GCP App Engine to EB Mapping -| App Engine Feature | EB Equivalent | -| ------------------------------- | ------------------------------------------ | -| App Engine Standard | EB with matching platform (Python, Node, etc.) | -| App Engine Flexible | EB Docker platform | -| `app.yaml` env vars | EB environment properties | -| `cron.yaml` | EB worker + EventBridge scheduled events | -| Task queues | EB worker + SQS | -| `instance_class` (F1/F2/F4) | Instance type selection (t3.small/medium) | -| Automatic scaling min/max | EB auto-scaling min/max instances | +| App Engine Feature | EB Equivalent | +| --------------------------- | --------------------------------------------------- | +| App Engine Standard | EB with matching platform (Python, Node, etc.) | +| App Engine Flexible | EB Docker platform | +| `app.yaml` env vars | EB environment properties | +| `cron.yaml` | EB worker + EventBridge scheduled events | +| Task queues | EB worker + SQS | +| `instance_class` (F1/F2/F4) | Instance type selection (see Sizing Defaults below) | +| Automatic scaling min/max | EB auto-scaling min/max instances | + +### Where the config comes from (Terraform) + +In Terraform, `google_app_engine_application` is only the **container** for an app — it carries the project/location, not the workload config. The `runtime`, `instance_class`, `env`, and scaling settings live on the **`google_app_engine_standard_app_version`** / **`google_app_engine_flexible_app_version`** resources (one per App Engine _service_/version). + +When mapping, treat the app_version resources as **config sources for the EB mapping**, not skipped resources: + +- **One EB environment per App Engine service.** A multi-service App Engine app (multiple `service` values across app_version resources) maps to **multiple EB environments** under one EB application — do **not** collapse it to a single mapping. +- Read `runtime` → EB platform (via Platform Detection Rules above). +- Read `instance_class` → EB instance type (see Sizing Defaults). +- Read `automatic_scaling` / `manual_scaling` → EB min/max instances. +- Read `env_variables` → EB environment properties. + +If **only** `google_app_engine_application` is present (no app_version resources — e.g. billing-only or partial Terraform), map a single EB environment and detect the platform/runtime from the app source instead, noting the assumption in `warnings`. ## Sizing Defaults -| Environment | Type | Instance | ALB | Min Instances | Multi-AZ | -| ----------- | --------------- | ----------- | --- | ------------- | -------- | -| Dev | SingleInstance | t3.small | No | 1 | No | -| Prod | LoadBalanced | t3.medium+ | Yes | 2 | Yes | +| Environment | Type | Instance | ALB | Min Instances | Multi-AZ | +| ----------- | -------------- | ---------- | --- | ------------- | -------- | +| Dev | SingleInstance | t3.small | No | 1 | No | +| Prod | LoadBalanced | t3.medium+ | Yes | 2 | Yes | ## Output Schema +One mapping **per App Engine service** (per app_version resource). `runtime` and `instance_class` are read from the `*_app_version` resource, not from the parent `google_app_engine_application`. `instance_type` follows the Sizing Defaults table (LoadBalanced → t3.medium+; SingleInstance → t3.small). + ```json { - "gcp_type": "google_app_engine_application", - "gcp_address": "example-app", + "gcp_type": "google_app_engine_standard_app_version", + "gcp_address": "default/v1", "gcp_config": { + "service": "default", "runtime": "python39", "instance_class": "F2" }, "aws_service": "Elastic Beanstalk", "aws_config": { - "platform": "Python 3.9", + "eb_application": "example-app", + "eb_environment": "default", + "platform": "Python 3.9 running on 64bit Amazon Linux 2023", "environment_type": "LoadBalanced", - "instance_type": "t3.small", + "instance_type": "t3.medium", "region": "us-east-1" }, "confidence": "deterministic", - "rationale": "Direct Mapping: google_app_engine_application → Elastic Beanstalk (compute_model absent or managed_platform)" + "rationale": "Direct Mapping: App Engine service (google_app_engine_standard_app_version) → Elastic Beanstalk environment (compute_model absent or managed_platform)" } ``` diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md index cf764390..444dfe92 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md @@ -36,22 +36,22 @@ JSON artifacts **must** keep the `confidence` string values above. When speaking ## Direct Mappings Table -| GCP Service | AWS Service | Conditions | Notes | -| ------------------------------------------- | -------------------- | ---------- | ---------------------------------------------------- | -| `google_storage_bucket` | S3 | Always | 1:1 mapping; preserve ACL/versioning/lifecycle rules | -| `google_cloud_run_service` | Fargate | Always | Preferred container runtime target | -| `google_cloud_run_v2_service` | Fargate | Always | v2 API variant of Cloud Run | -| `google_cloudfunctions_function` | Lambda | Always | Gen 1 function mapping | -| `google_cloudfunctions2_function` | Lambda | Always | Gen 2 function mapping | -| `google_sql_database_instance` (SQL Server) | RDS SQL Server | Always | Always provisioned (no serverless) | -| `google_compute_network` | VPC | Always | 1:1; preserve CIDR ranges | -| `google_compute_firewall` | Security Group | Always | 1:1 rule mapping; adjust CIDR if needed | -| `google_dns_managed_zone` | Route 53 Hosted Zone | Always | Preserve zone name and records | -| `google_service_account` | IAM Role | Always | Map permissions directly; adjust service principals | -| `google_secret_manager_secret` | Secrets Manager | Always | Create secret metadata and IAM-scoped access | -| `google_secret_manager_secret_version` | Secrets Manager | Always | Carry current value or explicit migration TODO | -| `google_redis_instance` | ElastiCache Redis | Always | 1:1 mapping; preserve cluster mode and node type | -| `google_app_engine_application` | Elastic Beanstalk | `compute_model` absent or `"managed_platform"` | PaaS-to-PaaS; detect runtime from app.yaml. If `compute_model: "container_orchestration"` or `"serverless"`, use rubric in `compute.md` (confidence: `inferred`). | +| GCP Service | AWS Service | Conditions | Notes | +| ------------------------------------------- | -------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `google_storage_bucket` | S3 | Always | 1:1 mapping; preserve ACL/versioning/lifecycle rules | +| `google_cloud_run_service` | Fargate | Always | Preferred container runtime target | +| `google_cloud_run_v2_service` | Fargate | Always | v2 API variant of Cloud Run | +| `google_cloudfunctions_function` | Lambda | Always | Gen 1 function mapping | +| `google_cloudfunctions2_function` | Lambda | Always | Gen 2 function mapping | +| `google_sql_database_instance` (SQL Server) | RDS SQL Server | Always | Always provisioned (no serverless) | +| `google_compute_network` | VPC | Always | 1:1; preserve CIDR ranges | +| `google_compute_firewall` | Security Group | Always | 1:1 rule mapping; adjust CIDR if needed | +| `google_dns_managed_zone` | Route 53 Hosted Zone | Always | Preserve zone name and records | +| `google_service_account` | IAM Role | Always | Map permissions directly; adjust service principals | +| `google_secret_manager_secret` | Secrets Manager | Always | Create secret metadata and IAM-scoped access | +| `google_secret_manager_secret_version` | Secrets Manager | Always | Carry current value or explicit migration TODO | +| `google_redis_instance` | ElastiCache Redis | Always | 1:1 mapping; preserve cluster mode and node type | +| `google_app_engine_application` | Elastic Beanstalk | `compute_model` absent or `"managed_platform"` | PaaS-to-PaaS. One EB environment **per App Engine service**; read `runtime`/`instance_class`/scaling from the `*_app_version` resources (see `elastic-beanstalk.md`). If `compute_model: "container_orchestration"` or `"serverless"`, use rubric in `compute.md` (confidence: `inferred`). | ### Cloud SQL PostgreSQL / MySQL — NOT in Direct Mappings @@ -80,18 +80,18 @@ These GCP resources do **not** require AWS equivalents in v1.0: | `google_compute_address` | Elastic IPs managed by ALB/NAT (not standalone) | | `google_identity_platform_*` | Auth provider — keep existing solution, do not migrate to AWS Cognito or any AWS auth | | `google_firebase_auth_*` | Auth provider — keep existing solution, do not migrate to AWS Cognito or any AWS auth | -| `google_app_engine_standard_app_version` | Configuration of parent `google_app_engine_application`; no separate AWS resource | -| `google_app_engine_flexible_app_version` | Configuration of parent `google_app_engine_application`; no separate AWS resource | + +**Note — App Engine app_version resources are NOT skips.** `google_app_engine_standard_app_version` and `google_app_engine_flexible_app_version` are **config sources** for the App Engine → Elastic Beanstalk mapping, not skipped resources. They carry `runtime`, `instance_class`, `env_variables`, and scaling — exactly the data EB needs — which live on the _version_ resource, not on the parent `google_app_engine_application`. Map **one EB environment per App Engine service** (per app_version), so a multi-service app does not collapse into a single mapping. See `elastic-beanstalk.md` → "Where the config comes from (Terraform)". ## Preferred AWS Target Services For the workload categories below, always steer toward the **preferred** AWS service. If rubric evaluation or LLM reasoning would select a non-preferred service, substitute the preferred alternative instead. -| Workload Category | Preferred AWS Target | Rationale | -| ----------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | -| Containerized workloads | Fargate (default), Lambda (event-driven), EKS (K8s required) | Deeper integration with VPC, ALB, IAM, and auto-scaling than lighter-weight alternatives | -| PaaS / managed platform | Elastic Beanstalk (when `compute_model: "managed_platform"` or App Engine source) | Preserves managed platform model; AWS manages deployments, scaling, patching, monitoring | -| Third-party auth in use | Keep existing auth provider | Startups using Auth0, Firebase Auth, Clerk, etc. should preserve their existing investment | +| Workload Category | Preferred AWS Target | Rationale | +| ----------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| Containerized workloads | Fargate (default), Lambda (event-driven), EKS (K8s required) | Deeper integration with VPC, ALB, IAM, and auto-scaling than lighter-weight alternatives | +| PaaS / managed platform | Elastic Beanstalk (when `compute_model: "managed_platform"` or App Engine source) | Preserves managed platform model; AWS manages deployments, scaling, patching, monitoring | +| Third-party auth in use | Keep existing auth provider | Startups using Auth0, Firebase Auth, Clerk, etc. should preserve their existing investment | **Enforcement:** Check every resource mapping against this table before writing to `aws-design.json` or `aws-design-billing.json`. If a mapping would select a non-preferred service, substitute the preferred alternative. diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/index.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/index.md index c94ef9b4..ae75c466 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/index.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/index.md @@ -4,12 +4,12 @@ ## Compute Services -| GCP Service | Resource Type | Reference File | Typical AWS target | -| ------------------- | -------------------------------- | -------------- | ------------------ | -| Cloud Run | `google_cloud_run_service` | `compute.md` | Fargate | -| Cloud Functions | `google_cloudfunctions_function` | `compute.md` | Lambda | -| Compute Engine (VM) | `google_compute_instance` | `compute.md` | EC2 or Fargate | -| GKE | `google_container_cluster` | `compute.md` | EKS | +| GCP Service | Resource Type | Reference File | Typical AWS target | +| ------------------- | -------------------------------- | -------------- | -------------------------------------- | +| Cloud Run | `google_cloud_run_service` | `compute.md` | Fargate | +| Cloud Functions | `google_cloudfunctions_function` | `compute.md` | Lambda | +| Compute Engine (VM) | `google_compute_instance` | `compute.md` | EC2 or Fargate | +| GKE | `google_container_cluster` | `compute.md` | EKS | | App Engine | `google_app_engine_application` | `compute.md` | Elastic Beanstalk (default) or Fargate | ## Database Services diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify-compute.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify-compute.md index 634835ca..cc256ce5 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify-compute.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify-compute.md @@ -50,12 +50,12 @@ Note: This question does NOT affect Cloud Run resources. Cloud Run maps to Farga > C) Serverless — Event-driven functions, scale-to-zero, stateless > D) I don't know — recommend the best fit -| Answer | Recommendation Impact | -| ------------------- | ----------------------------------------------------------------------------------------- | -| Managed platform | Elastic Beanstalk — preserves PaaS model, AWS manages deployments/scaling/patching | -| Container control | ECS Fargate — direct container management with full VPC/ALB/IAM integration | -| Serverless | Lambda — event-driven, stateless functions with scale-to-zero | -| I don't know | Default: Elastic Beanstalk (PaaS-to-PaaS, closest match to App Engine) | +| Answer | Recommendation Impact | +| ----------------- | ---------------------------------------------------------------------------------- | +| Managed platform | Elastic Beanstalk — preserves PaaS model, AWS manages deployments/scaling/patching | +| Container control | ECS Fargate — direct container management with full VPC/ALB/IAM integration | +| Serverless | Lambda — event-driven, stateless functions with scale-to-zero | +| I don't know | Default: Elastic Beanstalk (PaaS-to-PaaS, closest match to App Engine) | Interpret: diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify.md index 6b0b4fa9..7bb6aba9 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify.md @@ -670,32 +670,32 @@ After writing `preferences.json`, delete `$MIGRATION_DIR/preferences-draft.json` ## Defaults Table -| Question | Default | Constraint | -| ----------------------- | -------------------- | ------------------------------------------------- | -| Q1 — Location | A (single region) | `target_region`: closest AWS region to GCP region | -| Q2 — Compliance | A (none) | no constraint | -| Q3 — GCP spend | B ($1K–$5K) | `gcp_monthly_spend: "$1K-$5K"` | -| Q4 — Funding stage | _(skip in IDE mode)_ | no constraint | -| Q5 — Multi-cloud | B (AWS-only) | no constraint | -| Q6 — Uptime | B (significant) | `availability: "multi-az"` | -| Q7 — Maintenance | D (flexible) | `cutover_strategy: "flexible"` | -| Q7b — Compute model | A (managed platform) | `compute_model: "managed_platform"` (App Engine only; skipped if no App Engine) | -| Q8 — K8s sentiment | B (neutral) | `kubernetes: "eks-or-ecs"` | -| Q9 — WebSocket | B (no) | no constraint | -| Q10 — Cloud Run traffic | C (24/7) | `cloud_run_traffic_pattern: "constant-24-7"` | -| Q11 — Cloud Run spend | B ($100–$500) | `cloud_run_monthly_spend: "$100-$500"` | -| Q12 — DB traffic | A (steady) | `database_traffic: "steady"` | -| Q13 — DB I/O | B (medium) | `db_io_workload: "medium"` | -| Q13b — DB size | E (unknown) | `db_size: "unknown"` → default to pgcopydb | -| Q14 — AI framework | _(auto-detect)_ | `ai_framework` from code detection | -| Q15 — AI spend | B ($500–$2K) | `ai_monthly_spend: "$500-$2K"` | -| Q16 — AI priority | E (balanced) | `ai_priority: "balanced"` | -| Q17 — Critical feature | J (none) | no additional override | -| Q18 — Volume + cost | A (low + quality) | `ai_token_volume: "low"` | -| Q19 — Current model | _(auto-detect)_ | `ai_model_baseline` from code detection | -| Q20 — Input types | A (text only) | no constraint | -| Q21 — AI latency | B (important) | `ai_latency: "important"` | -| Q22 — Task complexity | B (moderate) | `ai_complexity: "moderate"` | +| Question | Default | Constraint | +| ----------------------- | -------------------- | ------------------------------------------------------------------------------- | +| Q1 — Location | A (single region) | `target_region`: closest AWS region to GCP region | +| Q2 — Compliance | A (none) | no constraint | +| Q3 — GCP spend | B ($1K–$5K) | `gcp_monthly_spend: "$1K-$5K"` | +| Q4 — Funding stage | _(skip in IDE mode)_ | no constraint | +| Q5 — Multi-cloud | B (AWS-only) | no constraint | +| Q6 — Uptime | B (significant) | `availability: "multi-az"` | +| Q7 — Maintenance | D (flexible) | `cutover_strategy: "flexible"` | +| Q7b — Compute model | A (managed platform) | `compute_model: "managed_platform"` (App Engine only; skipped if no App Engine) | +| Q8 — K8s sentiment | B (neutral) | `kubernetes: "eks-or-ecs"` | +| Q9 — WebSocket | B (no) | no constraint | +| Q10 — Cloud Run traffic | C (24/7) | `cloud_run_traffic_pattern: "constant-24-7"` | +| Q11 — Cloud Run spend | B ($100–$500) | `cloud_run_monthly_spend: "$100-$500"` | +| Q12 — DB traffic | A (steady) | `database_traffic: "steady"` | +| Q13 — DB I/O | B (medium) | `db_io_workload: "medium"` | +| Q13b — DB size | E (unknown) | `db_size: "unknown"` → default to pgcopydb | +| Q14 — AI framework | _(auto-detect)_ | `ai_framework` from code detection | +| Q15 — AI spend | B ($500–$2K) | `ai_monthly_spend: "$500-$2K"` | +| Q16 — AI priority | E (balanced) | `ai_priority: "balanced"` | +| Q17 — Critical feature | J (none) | no additional override | +| Q18 — Volume + cost | A (low + quality) | `ai_token_volume: "low"` | +| Q19 — Current model | _(auto-detect)_ | `ai_model_baseline` from code detection | +| Q20 — Input types | A (text only) | no constraint | +| Q21 — AI latency | B (important) | `ai_latency: "important"` | +| Q22 — Task complexity | B (moderate) | `ai_complexity: "moderate"` | --- diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/shared/pricing-cache.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/shared/pricing-cache.md index 754de6ca..b8fb119a 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/shared/pricing-cache.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/shared/pricing-cache.md @@ -32,18 +32,18 @@ Linux/x86, on-demand. ### Elastic Beanstalk -| Metric | Rate | -| ------ | ---- | +| Metric | Rate | +| ----------- | ------------------------------------------------- | | Service fee | $0.00 (free — no additional charge for EB itself) | Costs are the underlying resources (EC2, ALB, EBS, CloudWatch). Typical estimates: -| Profile | Underlying Resources | Estimated Monthly | -| ------- | -------------------- | ----------------- | -| Dev (single instance, t3.small, no ALB) | EC2 t3.small | ~$15 | -| Dev + RDS (t3.small + db.t4g.micro) | EC2 + Aurora Serverless | ~$30–50 | -| Prod (load-balanced, 2× t3.medium, ALB) | 2× EC2 + ALB + EBS | ~$95–120 | -| Prod + RDS (2× t3.medium, ALB, db.t4g.medium) | EC2 + ALB + Aurora | ~$175–220 | +| Profile | Underlying Resources | Estimated Monthly | +| --------------------------------------------- | -------------------------------- | ----------------- | +| Dev (single instance, t3.small, no ALB) | EC2 t3.small | ~$15 | +| Dev + RDS (t3.small + db.t4g.micro) | EC2 + RDS db.t4g.micro | ~$30–50 | +| Prod (load-balanced, 2× t3.medium, ALB) | 2× EC2 + ALB + EBS | ~$95–120 | +| Prod + RDS (2× t3.medium, ALB, db.t4g.medium) | 2× EC2 + ALB + RDS db.t4g.medium | ~$175–220 | ### EKS From b6c651aa0f4e6317a2369f67ff63a60fa0d7a55c Mon Sep 17 00:00:00 2001 From: Paul Pollack Date: Tue, 14 Jul 2026 12:35:46 -0400 Subject: [PATCH 4/8] =?UTF-8?q?fix(gcp-to-aws):=20wire=20App=20Engine=20?= =?UTF-8?q?=E2=86=92=20EB=20fan-out=20into=20design=20traversal=20(PR=20#1?= =?UTF-8?q?01)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses remaining EB review feedback from PR #101, focused on the App Engine → Elastic Beanstalk mapping (reviewer's main concern). App Engine workload config (runtime, instance_class, scaling, env) lives on the google_app_engine_{standard,flexible}_app_version resources, not the parent google_app_engine_application. The prior pass documented this but the design traversal didn't act on it, so a multi-service app still collapsed into one mapping. - classification-rules.md: reclassify app_version resources from Skip to SECONDARY config sources for the parent's EB mapping - design-infra.md: add an App Engine fan-out step in Pass 1 that scans the full inventory for app_version resources (they link to the parent only by shared project, not by ID, so serves[]/clustering is unreliable), groups by service, and emits one EB environment per service (picking the serving/most-recent version per service) - elastic-beanstalk.md: rework 'config comes from Terraform' section and output schema to a per-service example; drive instance_type from environment type (instance_class / Flexible resources block as a right-sizing nudge only), listing all three scaling blocks - fast-path.md / compute.md: reconcile the notes and examples with the fan-out mechanism; stop showing runtime on the parent resource Verified field names and the parent-linkage behavior against the Terraform Google provider docs. Docs/reference only; no runtime code. mise build tasks pass locally (lint:md, lint:types, lint:frontmatter, test 45/45, fmt:check, bandit, gitleaks); semgrep not installed locally, runs in CI. --- .../terraform/classification-rules.md | 2 + .../references/design-refs/compute.md | 6 +- .../design-refs/elastic-beanstalk.md | 100 ++++++++++++------ .../references/design-refs/fast-path.md | 2 +- .../references/phases/design/design-infra.md | 1 + 5 files changed, 72 insertions(+), 39 deletions(-) diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/clustering/terraform/classification-rules.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/clustering/terraform/classification-rules.md index f03ddace..d46d56eb 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/clustering/terraform/classification-rules.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/clustering/terraform/classification-rules.md @@ -100,6 +100,8 @@ Match resource type against secondary classification table. Each match assigns a - `google_secret_manager_secret_version` — Secret value - `google_dns_record_set` — DNS record - `google_monitoring_notification_channel` — Alert notification target +- `google_app_engine_standard_app_version` — App Engine service/version. Config source for the parent `google_app_engine_application`, carrying `runtime`, `instance_class`, `env_variables`, and scaling — the data the EB mapping needs. Note: these resources do **not** reference the parent by ID (link is the shared `project`), so `serves[]` may be empty and they may cluster separately; the App Engine fan-out step in `phases/design/design-infra.md` locates them by scanning the full inventory. **Not excluded.** +- `google_app_engine_flexible_app_version` — App Engine Flexible service/version (config source for the parent; same handling as the standard variant) ### Encryption (`encryption`) diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md index 98793ec7..4786c60f 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md @@ -115,8 +115,8 @@ Apply in order; first match wins: ### Example 4a: App Engine (standard Python web app, default preference) -- GCP: `google_app_engine_standard_app_version` (service=default, runtime=python39, instance_class=F2) under `google_app_engine_application` -- Note: `runtime`/`instance_class` come from the `*_app_version` resource, not the parent `google_app_engine_application`. Map one EB environment per App Engine service. +- GCP: `google_app_engine_application` with one service (`default`) whose `google_app_engine_standard_app_version` has runtime=python39, instance_class=F2 +- Note: `runtime`/`instance_class` come from the `*_app_version` resource, not the parent. The App Engine fan-out step (`phases/design/design-infra.md`) emits one EB environment per service; `gcp_type` stays `google_app_engine_application`. - Signals: PaaS deployment, `compute_model` absent or `"managed_platform"` - Fast-path condition met: `compute_model` not set to `"container_orchestration"` or `"serverless"` - → **AWS: Elastic Beanstalk (Python 3.9, LoadBalanced, t3.medium)** — LoadBalanced uses t3.medium+ per `elastic-beanstalk.md` Sizing Defaults @@ -124,7 +124,7 @@ Apply in order; first match wins: ### Example 4b: App Engine (user chose container orchestration) -- GCP: `google_app_engine_standard_app_version` (service=default, runtime=python39, instance_class=F2) under `google_app_engine_application` +- GCP: `google_app_engine_application` with one service (`default`), app_version runtime=python39, instance_class=F2 - Signals: PaaS deployment, but `compute_model: "container_orchestration"` in preferences - Fast-path condition NOT met: falls through to rubric - Criterion 1 (Eliminators): EB blocked (user chose container orchestration) diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md index d40c5182..5fd7aee9 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md @@ -105,60 +105,90 @@ Migrated App Engine apps already read `PORT` (App Engine sets it too), so the fi ## GCP App Engine to EB Mapping -| App Engine Feature | EB Equivalent | -| --------------------------- | --------------------------------------------------- | -| App Engine Standard | EB with matching platform (Python, Node, etc.) | -| App Engine Flexible | EB Docker platform | -| `app.yaml` env vars | EB environment properties | -| `cron.yaml` | EB worker + EventBridge scheduled events | -| Task queues | EB worker + SQS | -| `instance_class` (F1/F2/F4) | Instance type selection (see Sizing Defaults below) | -| Automatic scaling min/max | EB auto-scaling min/max instances | +| App Engine Feature | EB Equivalent | +| ---------------------------- | ----------------------------------------------------------------- | +| App Engine Standard | EB with matching platform (Python, Node, etc.) | +| App Engine Flexible | EB Docker platform | +| `app.yaml` env vars | EB environment properties | +| `cron.yaml` | EB worker + EventBridge scheduled events | +| Task queues | EB worker + SQS | +| `instance_class` (F/B tiers) | Right-sizing signal for instance type (see Sizing Defaults below) | +| Automatic scaling min/max | EB auto-scaling min/max instances | ### Where the config comes from (Terraform) -In Terraform, `google_app_engine_application` is only the **container** for an app — it carries the project/location, not the workload config. The `runtime`, `instance_class`, `env`, and scaling settings live on the **`google_app_engine_standard_app_version`** / **`google_app_engine_flexible_app_version`** resources (one per App Engine _service_/version). +In Terraform, `google_app_engine_application` is only the **container** for an app — it carries the project/location, not the workload config. The `runtime`, `instance_class`, `env_variables`, and scaling settings live on the **`google_app_engine_standard_app_version`** / **`google_app_engine_flexible_app_version`** resources. Each such resource is one **version** of one **service** (identified by its `service` argument; a service can have many versions). -When mapping, treat the app_version resources as **config sources for the EB mapping**, not skipped resources: +During discovery these version resources are classified SECONDARY (`configuration`), and the App Engine fan-out step in `phases/design/design-infra.md` reads them when mapping the parent. Treat them as **config sources for the EB mapping**, not skipped resources: -- **One EB environment per App Engine service.** A multi-service App Engine app (multiple `service` values across app_version resources) maps to **multiple EB environments** under one EB application — do **not** collapse it to a single mapping. -- Read `runtime` → EB platform (via Platform Detection Rules above). -- Read `instance_class` → EB instance type (see Sizing Defaults). -- Read `automatic_scaling` / `manual_scaling` → EB min/max instances. +- **One EB environment per App Engine _service_, not per version.** Group app_version resources by their `service` value. Multiple versions of the same service (e.g. `v1`, `v2` both with `service = "myapp"`) collapse to **one** EB environment — pick the serving/most-recent version for config (prefer `serving_status = "SERVING"`; otherwise the highest `version_id`). Distinct `service` values (e.g. `default`, `worker`) each become a **separate** EB environment under one EB application. Do not emit one env per version, and do not collapse distinct services into one. +- Read `runtime` → EB platform (via Platform Detection Rules above). For Flexible, `flexible_runtime_settings.operating_system` / `runtime_version` may further qualify it. +- Read `automatic_scaling` / `basic_scaling` / `manual_scaling` → EB min/max instances (whichever block is present; Standard defaults to automatic). - Read `env_variables` → EB environment properties. +- Derive `instance_type` from **environment type** via Sizing Defaults (below); use the Standard `instance_class` (F/B tiers) or the Flexible `resources` block (`cpu` / `memory_gb`) only to right-size within that band. If **only** `google_app_engine_application` is present (no app_version resources — e.g. billing-only or partial Terraform), map a single EB environment and detect the platform/runtime from the app source instead, noting the assumption in `warnings`. ## Sizing Defaults +Instance type is driven by **environment type** (which follows from the availability/uptime preference), not by a direct App Engine instance-class lookup: + | Environment | Type | Instance | ALB | Min Instances | Multi-AZ | | ----------- | -------------- | ---------- | --- | ------------- | -------- | | Dev | SingleInstance | t3.small | No | 1 | No | | Prod | LoadBalanced | t3.medium+ | Yes | 2 | Yes | +**Right-sizing with `instance_class`:** App Engine instance classes have no 1:1 EC2 equivalent (App Engine sizes by memory/CPU limits, not instance families). Use the class only to nudge within the environment band above — start at the band default and step up one size for larger classes (e.g. F4/F4_1G, or B4/B8), rather than mapping each class to a specific type. When unsure, keep the band default and note the assumption in `warnings`. + ## Output Schema -One mapping **per App Engine service** (per app_version resource). `runtime` and `instance_class` are read from the `*_app_version` resource, not from the parent `google_app_engine_application`. `instance_type` follows the Sizing Defaults table (LoadBalanced → t3.medium+; SingleInstance → t3.small). +The design phase emits **one mapping per App Engine service** (see the App Engine fan-out step in `phases/design/design-infra.md`). `gcp_type` stays `google_app_engine_application` (the Direct Mappings row), and `aws_config.source_service` records which App Engine service the environment came from. `runtime` and `instance_class` are read from that service's `*_app_version` resource, not from the parent; `instance_type` follows the Sizing Defaults table (LoadBalanced → t3.medium+; SingleInstance → t3.small). + +Example — a two-service app (`default` + `worker`) produces two mappings under one EB application: ```json -{ - "gcp_type": "google_app_engine_standard_app_version", - "gcp_address": "default/v1", - "gcp_config": { - "service": "default", - "runtime": "python39", - "instance_class": "F2" - }, - "aws_service": "Elastic Beanstalk", - "aws_config": { - "eb_application": "example-app", - "eb_environment": "default", - "platform": "Python 3.9 running on 64bit Amazon Linux 2023", - "environment_type": "LoadBalanced", - "instance_type": "t3.medium", - "region": "us-east-1" +[ + { + "gcp_type": "google_app_engine_application", + "gcp_address": "example-app", + "gcp_config": { + "source_service": "default", + "runtime": "python39", + "instance_class": "F2" + }, + "aws_service": "Elastic Beanstalk", + "aws_config": { + "eb_application": "example-app", + "eb_environment": "default", + "source_service": "default", + "platform": "Python 3.9 running on 64bit Amazon Linux 2023", + "environment_type": "LoadBalanced", + "instance_type": "t3.medium", + "region": "us-east-1" + }, + "confidence": "deterministic", + "rationale": "Direct Mapping: App Engine service 'default' → Elastic Beanstalk environment (compute_model absent or managed_platform)" }, - "confidence": "deterministic", - "rationale": "Direct Mapping: App Engine service (google_app_engine_standard_app_version) → Elastic Beanstalk environment (compute_model absent or managed_platform)" -} + { + "gcp_type": "google_app_engine_application", + "gcp_address": "example-app", + "gcp_config": { + "source_service": "worker", + "runtime": "python39", + "instance_class": "B2" + }, + "aws_service": "Elastic Beanstalk", + "aws_config": { + "eb_application": "example-app", + "eb_environment": "worker", + "source_service": "worker", + "platform": "Python 3.9 running on 64bit Amazon Linux 2023", + "environment_type": "SingleInstance", + "instance_type": "t3.small", + "region": "us-east-1" + }, + "confidence": "deterministic", + "rationale": "Direct Mapping: App Engine service 'worker' → Elastic Beanstalk environment (compute_model absent or managed_platform)" + } +] ``` diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md index 444dfe92..8a36cfc7 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md @@ -81,7 +81,7 @@ These GCP resources do **not** require AWS equivalents in v1.0: | `google_identity_platform_*` | Auth provider — keep existing solution, do not migrate to AWS Cognito or any AWS auth | | `google_firebase_auth_*` | Auth provider — keep existing solution, do not migrate to AWS Cognito or any AWS auth | -**Note — App Engine app_version resources are NOT skips.** `google_app_engine_standard_app_version` and `google_app_engine_flexible_app_version` are **config sources** for the App Engine → Elastic Beanstalk mapping, not skipped resources. They carry `runtime`, `instance_class`, `env_variables`, and scaling — exactly the data EB needs — which live on the _version_ resource, not on the parent `google_app_engine_application`. Map **one EB environment per App Engine service** (per app_version), so a multi-service app does not collapse into a single mapping. See `elastic-beanstalk.md` → "Where the config comes from (Terraform)". +**Note — App Engine app_version resources are NOT skips.** `google_app_engine_standard_app_version` and `google_app_engine_flexible_app_version` are **config sources** for the App Engine → Elastic Beanstalk mapping, not skipped resources. They carry `runtime`, `instance_class`, `env_variables`, and scaling — exactly the data EB needs — which live on the _version_ resource, not on the parent `google_app_engine_application`. Discovery classifies them SECONDARY (`configuration`); because they link to the parent only by shared `project` (no ID reference), `serves[]` may be empty and they may cluster separately, so the **App Engine fan-out** step in `phases/design/design-infra.md` locates them by scanning the full inventory and emits **one EB environment per App Engine service** (grouping multiple versions of a service into one), so a multi-service app does not collapse into a single mapping. See `elastic-beanstalk.md` → "Where the config comes from (Terraform)". ## Preferred AWS Target Services diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md index ce580e61..5172191f 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md @@ -28,6 +28,7 @@ For each PRIMARY resource in the cluster: - `Always` → conditions met. - Conditional rows (e.g., `google_app_engine_application` requires `compute_model` absent or `"managed_platform"`): read `preferences.json → design_constraints.compute_model.value`. If the condition is NOT met (e.g., user chose `"container_orchestration"` or `"serverless"`), treat as **not found** — proceed to Pass 2. 4. If found and conditions match: assign AWS service with confidence = **`deterministic`**. Set `human_expertise_required: false` (no Direct Mapping row requires it). + - **App Engine fan-out (`google_app_engine_application` only):** the parent resource carries no workload config — `runtime`, `instance_class`, `env_variables`, and scaling live on the `google_app_engine_standard_app_version` / `google_app_engine_flexible_app_version` resources. **These do not reference the parent by ID** (App Engine allows one app per project, so the link is the shared `project`), so they are **not** reliably in the parent's cluster or `serves[]`. To find them, scan the **entire `gcp-resource-inventory.json`** (across all clusters) for app_version resources — do not rely on cluster membership. Match to this parent by shared `project` when that field is present; if `project` is absent (provider-level in Terraform) or there is only one `google_app_engine_application` in the inventory, treat **all** app_version resources as belonging to it. **Group the app_version resources by their `service` value**, then emit **one Elastic Beanstalk environment mapping per distinct `service`** (grouped under a single EB application named for the parent). Multiple versions of the same service (e.g. `v1` and `v2`, both `service = "myapp"`) are **one** EB environment — do not emit one per version; pick the serving/most-recent version for config (prefer `serving_status = "SERVING"`, else highest `version_id`). For each service, read `runtime` → EB platform and the scaling block (`automatic_scaling` / `basic_scaling` / `manual_scaling`) → EB min/max from that version resource; derive `instance_type` from the environment type via Sizing Defaults (use `instance_class` or the Flexible `resources` block only to right-size within the band) — see `design-refs/elastic-beanstalk.md`. Each emitted mapping keeps `gcp_type: "google_app_engine_application"` with `confidence: "deterministic"` and records the source `service` in `aws_config`. Mark the consumed app_version resources as mapped so Pass 2 does not re-process them as standalone SECONDARY resources. If **no** app_version resources are present (billing-only or partial Terraform), emit a single EB environment and detect the platform from app source, noting the assumption in `warnings`. 5. If `gcp_type` is `google_sql_database_instance` with PostgreSQL or MySQL engine: **always proceed to Pass 2** (Cloud SQL is not in Direct Mappings — see `fast-path.md`). Confidence = **`inferred`** after rubric. 6. If not found (or conditions not met): proceed to Pass 2 (confidence will be **`inferred`** after rubric, or **`billing_inferred`** on the billing-only path). From 06545778bf7ebbee90bbadd9b985745ccfffbbc0 Mon Sep 17 00:00:00 2001 From: Paul Pollack Date: Tue, 21 Jul 2026 09:56:17 -0400 Subject: [PATCH 5/8] fix(gcp-to-aws): address second-round PR #101 review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert an out-of-scope GKE routing regression and apply reviewer polish (herosjourney, icarthick). - compute.md: revert the GKE Signals bullet to main's Fargate default — a google_container_cluster in inventory no longer forces EKS over the user's Fargate/Q8 preference (flagged by two reviewers; also contradicted the file's own 6-criteria rubric). Add 'preference override, not a technical blocker' note to the App Engine Eliminators row; add a one-line note that deterministic mappings omit rubric_applied. - elastic-beanstalk.md: add 'Discovery Inputs' section clarifying Terraform (full per-service EB) vs billing (coarse) vs app-code/gcloud-live (not yet wired — #149 follow-up); add Worker-tier note (skill uses WebServer tier only, aligning with heroku-to-aws). - design-billing.md: App Engine billing row Fargate -> Elastic Beanstalk so the billing path matches the new default. - design-infra.md: convert the App Engine fan-out step from a dense paragraph to an ordered sub-step list for reliable single-pass execution. - SKILL.md: tighten elastic-beanstalk.md load condition — require App Engine in inventory; skip even if compute_model is set. Verified: fmt:check, lint:md, lint:types, test 57/57, lint:frontmatter, gitleaks all pass. --- .../skills/gcp-to-aws/SKILL.md | 20 +++++++++--------- .../references/design-refs/compute.md | 21 ++++++++++--------- .../design-refs/elastic-beanstalk.md | 13 ++++++++++++ .../phases/design/design-billing.md | 2 +- .../references/phases/design/design-infra.md | 9 +++++++- 5 files changed, 43 insertions(+), 22 deletions(-) diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md index e98edda2..848c759c 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md @@ -33,16 +33,16 @@ Each phase loads reference files on demand. To keep per-turn context manageable **Conditional reference files (load ONLY when condition is true):** -| File | Condition | -| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `design-refs/ai-gemini-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"gemini"` or `"both"` | -| `design-refs/ai-openai-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"openai"` or `"both"` | -| `design-refs/ai-anthropic-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"anthropic"` | -| `design-refs/ai.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"other"` | -| `design-refs/elastic-beanstalk.md` | `google_app_engine_application` in inventory OR `compute_model == "managed_platform"` in preferences. Supplementary reference for EB configuration detail (platforms, IAM, VPC, deployment policies). Does not replace `compute.md` — both may be needed in mixed projects. | -| `design-refs/design-ref-harness.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "harness"` | -| `design-refs/design-ref-agentic-to-agentcore.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "strands"` | -| `shared/retarget-gotchas.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "retarget"` | +| File | Condition | +| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `design-refs/ai-gemini-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"gemini"` or `"both"` | +| `design-refs/ai-openai-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"openai"` or `"both"` | +| `design-refs/ai-anthropic-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"anthropic"` | +| `design-refs/ai.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"other"` | +| `design-refs/elastic-beanstalk.md` | `google_app_engine_application` in inventory (optionally with `compute_model == "managed_platform"` in preferences). Supplementary reference for EB configuration detail (platforms, IAM, VPC, deployment policies). Does not replace `compute.md` — both may be needed in mixed projects. Skip when no App Engine is in inventory, even if `compute_model` is set. | +| `design-refs/design-ref-harness.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "harness"` | +| `design-refs/design-ref-agentic-to-agentcore.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "strands"` | +| `shared/retarget-gotchas.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "retarget"` | When adding new reference files, verify the phase's total loaded instructions remain under budget. If a new file would exceed ~800 lines when combined with other loaded refs, split it or make it conditional. diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md index 4786c60f..a6682bc1 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md @@ -11,14 +11,14 @@ ## Eliminators (Hard Blockers) -| GCP Service | AWS | Blocker | -| --------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Cloud Run | Lambda | Execution time >15 min → use Fargate | -| Cloud Run | Fargate | GPU workload or >16 vCPU or >120 GB memory → use EC2 | -| Cloud Functions | Lambda | Python version not supported (e.g., Python 2.7) → use custom runtime on Fargate | -| GKE | EKS | Custom CRI incompatible → manual workaround or ECS | -| Any | App Runner | **Closed to new customers (April 30 2026).** Do not target App Runner for new migrations. Use Fargate (default), Lambda (event-driven), or EKS (K8s required). | -| App Engine | Elastic Beanstalk | `compute_model: "container_orchestration"` or `"serverless"` in preferences → do not use EB, fall through to Fargate or Lambda | +| GCP Service | AWS | Blocker | +| --------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Cloud Run | Lambda | Execution time >15 min → use Fargate | +| Cloud Run | Fargate | GPU workload or >16 vCPU or >120 GB memory → use EC2 | +| Cloud Functions | Lambda | Python version not supported (e.g., Python 2.7) → use custom runtime on Fargate | +| GKE | EKS | Custom CRI incompatible → manual workaround or ECS | +| Any | App Runner | **Closed to new customers (April 30 2026).** Do not target App Runner for new migrations. Use Fargate (default), Lambda (event-driven), or EKS (K8s required). | +| App Engine | Elastic Beanstalk | `compute_model: "container_orchestration"` or `"serverless"` in preferences → do not use EB, fall through to Fargate or Lambda _(preference override, not a technical blocker)_ | ## Signals (Decision Criteria) @@ -45,8 +45,7 @@ Note: Cloud Run maps to Fargate via deterministic fast-path ("Always"). The `com - **Kubernetes orchestration explicitly required** (`kubernetes = "eks-managed"` or `"eks-or-ecs"` in `preferences.json`) → EKS - **Default / no explicit K8s preference** (`kubernetes = "ecs-fargate"` or absent): - - If `gcp-resource-inventory.json` contains `google_container_cluster` → EKS (IaC signal shows K8s workload) - - Otherwise → **Fargate** (no K8s signal; lower-ops default) + - → **Fargate** (absent kubernetes preference resolves to Fargate, not EKS — teams that want EKS answer A or B in Clarify) ### App Engine @@ -134,6 +133,8 @@ Apply in order; first match wins: ## Output Schema +Deterministic (fast-path) mappings omit `rubric_applied`; inferred (rubric-based) mappings include it. + **Deterministic (fast-path) example:** ```json diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md index 5fd7aee9..b66693b2 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md @@ -2,6 +2,17 @@ **Applies to:** Google App Engine (Standard/Flexible) +## Discovery Inputs (what fires this mapping) + +App Engine → EB fidelity depends on how the workload was discovered: + +- **Terraform** (`google_app_engine_application` + `*_app_version` resources) → full mapping: Q7b fires, and the design fan-out emits one EB environment per service with per-service `runtime`/scaling/sizing. This is the primary path. +- **Billing export** (no Terraform) → coarse mapping via the billing design path (`design-billing.md`): App Engine still targets EB, but without per-service or runtime detail (`confidence: billing_inferred`). +- **App code only** (no Terraform, no billing) → no App Engine compute inventory is produced today, so no EB mapping fires. +- **Live `gcloud` discovery** → App Engine capture is **not yet wired** (tracked as a follow-up to the live-discovery work, PR #149); until then TF-less, billing-less App Engine projects surface App Engine as an unmapped asset rather than an EB target. + +This is not a "Terraform required" policy — it reflects which discovery paths currently produce the inventory the fast-path needs. Widening the non-Terraform paths is follow-up work. + ## Key Distinction - **EB is application management** (AWS manages lifecycle: provisioning, load balancing, scaling, patching) vs **ECS/Fargate as infrastructure management** (user manages task definitions, service configs, scaling policies). @@ -65,6 +76,8 @@ Detect from app source to select EB platform automatically: - **Web server** (default): Handles HTTP requests via ALB. Use for APIs, web apps, frontends. - **Worker**: Processes background jobs from SQS queue. No public endpoint. Use for async tasks, cron-like jobs, batch processing. +**This skill uses the Web server tier only.** All App Engine services map to WebServer-tier environments (SingleInstance or LoadBalanced per Sizing Defaults) — the Worker tier is not selected. App Engine background work (`cron.yaml`, task queues) maps to EventBridge + SQS alongside the web environment, not to an EB Worker environment. + ## Configuration **IaC tool:** AWS CLI (`aws elasticbeanstalk` commands). Do not use EB CLI (avoids install dependency on target machine). diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-billing.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-billing.md index 0aa33261..b2a13c0a 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-billing.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-billing.md @@ -56,7 +56,7 @@ Look up `gcp_service_type` in the table below. These are default mappings for co | `google_cloudfunctions_function` | Cloud Functions | Lambda | Fargate | | `google_compute_instance` | Compute Engine | EC2 | Fargate, ASG | | `google_container_cluster` | GKE | EKS | ECS, Fargate | -| `google_app_engine_application` | App Engine | Fargate | Amplify, Lambda | +| `google_app_engine_application` | App Engine | Elastic Beanstalk | Fargate (`compute_model: container_orchestration`), Lambda (`serverless`) | | `google_firestore_database` | Firestore | DynamoDB | — | | `google_bigquery_dataset` | BigQuery | **`Deferred — specialist engagement`** | **No** Athena/Redshift/Glue in automated output. **`human_expertise_required: true`**. User must engage **AWS account team** and/or **data analytics migration partner**. | | `google_compute_forwarding_rule` | Cloud Load Balancing | ALB | NLB | diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md index 5172191f..bc819aac 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md @@ -28,7 +28,14 @@ For each PRIMARY resource in the cluster: - `Always` → conditions met. - Conditional rows (e.g., `google_app_engine_application` requires `compute_model` absent or `"managed_platform"`): read `preferences.json → design_constraints.compute_model.value`. If the condition is NOT met (e.g., user chose `"container_orchestration"` or `"serverless"`), treat as **not found** — proceed to Pass 2. 4. If found and conditions match: assign AWS service with confidence = **`deterministic`**. Set `human_expertise_required: false` (no Direct Mapping row requires it). - - **App Engine fan-out (`google_app_engine_application` only):** the parent resource carries no workload config — `runtime`, `instance_class`, `env_variables`, and scaling live on the `google_app_engine_standard_app_version` / `google_app_engine_flexible_app_version` resources. **These do not reference the parent by ID** (App Engine allows one app per project, so the link is the shared `project`), so they are **not** reliably in the parent's cluster or `serves[]`. To find them, scan the **entire `gcp-resource-inventory.json`** (across all clusters) for app_version resources — do not rely on cluster membership. Match to this parent by shared `project` when that field is present; if `project` is absent (provider-level in Terraform) or there is only one `google_app_engine_application` in the inventory, treat **all** app_version resources as belonging to it. **Group the app_version resources by their `service` value**, then emit **one Elastic Beanstalk environment mapping per distinct `service`** (grouped under a single EB application named for the parent). Multiple versions of the same service (e.g. `v1` and `v2`, both `service = "myapp"`) are **one** EB environment — do not emit one per version; pick the serving/most-recent version for config (prefer `serving_status = "SERVING"`, else highest `version_id`). For each service, read `runtime` → EB platform and the scaling block (`automatic_scaling` / `basic_scaling` / `manual_scaling`) → EB min/max from that version resource; derive `instance_type` from the environment type via Sizing Defaults (use `instance_class` or the Flexible `resources` block only to right-size within the band) — see `design-refs/elastic-beanstalk.md`. Each emitted mapping keeps `gcp_type: "google_app_engine_application"` with `confidence: "deterministic"` and records the source `service` in `aws_config`. Mark the consumed app_version resources as mapped so Pass 2 does not re-process them as standalone SECONDARY resources. If **no** app_version resources are present (billing-only or partial Terraform), emit a single EB environment and detect the platform from app source, noting the assumption in `warnings`. + - **App Engine fan-out (`google_app_engine_application` only):** the parent resource carries no workload config — `runtime`, `instance_class`, `env_variables`, and scaling live on the `google_app_engine_standard_app_version` / `google_app_engine_flexible_app_version` resources. Execute these sub-steps in order: + + 1. **Locate the version resources.** They do **not** reference the parent by ID (App Engine allows one app per project, so the link is the shared `project`), so they are **not** reliably in the parent's cluster or `serves[]`. Scan the **entire `gcp-resource-inventory.json`** across all clusters for app_version resources — do not rely on cluster membership. + 2. **Attribute them to this parent.** Match by shared `project` when that field is present. If `project` is absent (provider-level in Terraform) or there is only one `google_app_engine_application` in the inventory, treat **all** app_version resources as belonging to it. + 3. **If none were found** (billing-only or partial Terraform): emit a single EB environment, detect the platform from app source, note the assumption in `warnings`, and skip the remaining sub-steps. + 4. **Group by `service`.** Multiple versions of the same service (e.g. `v1` and `v2`, both `service = "myapp"`) are **one** service → **one** EB environment. Pick the config-source version per service: prefer `serving_status = "SERVING"`, else the highest `version_id`. + 5. **Emit one mapping per distinct `service`**, all under a single EB application named for the parent. For each: `runtime` → EB platform; scaling block (`automatic_scaling` / `basic_scaling` / `manual_scaling`) → EB min/max; `instance_type` from environment type via Sizing Defaults (use `instance_class` or the Flexible `resources` block only to right-size within the band). See `design-refs/elastic-beanstalk.md`. + 6. **Finalize each mapping:** keep `gcp_type: "google_app_engine_application"`, `confidence: "deterministic"`, and record the source `service` in `aws_config`. Mark the consumed app_version resources as mapped so Pass 2 does not re-process them as standalone SECONDARY resources. 5. If `gcp_type` is `google_sql_database_instance` with PostgreSQL or MySQL engine: **always proceed to Pass 2** (Cloud SQL is not in Direct Mappings — see `fast-path.md`). Confidence = **`inferred`** after rubric. 6. If not found (or conditions not met): proceed to Pass 2 (confidence will be **`inferred`** after rubric, or **`billing_inferred`** on the billing-only path). From 8fdf65c8e9d2aa2d2a21e7b2aa56021d607642b0 Mon Sep 17 00:00:00 2001 From: Paul Pollack Date: Tue, 21 Jul 2026 12:57:03 -0400 Subject: [PATCH 6/8] =?UTF-8?q?feat(gcp-to-aws):=20wire=20App=20Engine=20?= =?UTF-8?q?=E2=86=92=20EB=20end-to-end=20(estimate=20+=20generate=20+=20si?= =?UTF-8?q?zing)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Third-round PR #101 review response. Makes the App Engine → Elastic Beanstalk feature complete across all phases and resolves the reviewer blockers. Design/routing: - Replace the Q6-availability EB sizing rule with source-config sizing (the repo's established pattern): environment type + instance size derive per-service from the app_version's own scaling block and instance_class (Standard) / resources block (Flexible). Q6 governs databases only. - Fix the preferred-target reversal: the PaaS/managed-platform row is gated on compute_model (absent/managed_platform); Q7b=B/C keeps its rubric-selected Fargate/Lambda target (explicit enforcement exemption). - Per-service unique gcp_address (parent TF address # service); carry min_instances + max_instances through design→generate. Estimate: - Add an Elastic Beanstalk cost row (EC2 × min_instances × 730 + ALB when LoadBalanced; /bin/zsh service fee), EB log-volume heuristic, and EB EC2 in the Graviton rollup. ALB row carves out LoadBalanced EB envs to avoid double-counting. Generate: - Route EB to compute.tf; emit aws_elastic_beanstalk_application + _environment with setting blocks (InstanceType, EnvironmentType, LoadBalancerType=application for ALB, ASG Min/MaxSize, IAM instance profile, VPC — public subnet for SingleInstance, private+ALB for LoadBalanced, env vars). Resolve solution_stack_name at generate time (do not paste the label). - Migration script: shared always-emitted preamble + independent ECR and EB source-deploy sections (EB builds from Dockerfile at deploy time — no ECR push); reconcile the IaC model (Terraform provisions, CLI deploys). Discovery: capture App Engine version fields (service, runtime, instance_class, scaling, serving_status, project, env_variables) into the inventory. Verified: fmt:check, lint:md (0), lint:types, test 57/57, lint:frontmatter, gitleaks all pass. Reviewed across two adversarial passes; both BLOCKERs and six follow-on findings resolved. --- .../terraform/classification-rules.md | 2 +- .../references/design-refs/compute.md | 8 +- .../design-refs/elastic-beanstalk.md | 105 +++++++++++------- .../references/design-refs/fast-path.md | 36 +++--- .../references/design-refs/index.md | 14 +-- .../references/phases/clarify/clarify.md | 1 + .../references/phases/design/design-infra.md | 11 +- .../phases/discover/discover-iac.md | 1 + .../phases/estimate/estimate-infra.md | 26 +++-- .../generate/generate-artifacts-infra.md | 25 +++-- .../generate/generate-artifacts-scripts.md | 67 +++++++++-- .../references/shared/pricing-cache.md | 14 ++- .../references/shared/schema-discover-iac.md | 23 ++++ 13 files changed, 224 insertions(+), 109 deletions(-) diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/clustering/terraform/classification-rules.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/clustering/terraform/classification-rules.md index d46d56eb..8b4dd8ad 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/clustering/terraform/classification-rules.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/clustering/terraform/classification-rules.md @@ -100,7 +100,7 @@ Match resource type against secondary classification table. Each match assigns a - `google_secret_manager_secret_version` — Secret value - `google_dns_record_set` — DNS record - `google_monitoring_notification_channel` — Alert notification target -- `google_app_engine_standard_app_version` — App Engine service/version. Config source for the parent `google_app_engine_application`, carrying `runtime`, `instance_class`, `env_variables`, and scaling — the data the EB mapping needs. Note: these resources do **not** reference the parent by ID (link is the shared `project`), so `serves[]` may be empty and they may cluster separately; the App Engine fan-out step in `phases/design/design-infra.md` locates them by scanning the full inventory. **Not excluded.** +- `google_app_engine_standard_app_version` — App Engine service/version. Config source for the parent `google_app_engine_application`, carrying `runtime`, `instance_class`, `env_variables`, and scaling — the data the EB mapping needs. Note: these resources do **not** reference the parent by ID (link is the shared `project`), so `serves[]` may be empty and they may cluster separately; the App Engine fan-out step in `phases/design/design-infra.md` locates them by scanning the full inventory. **Not excluded from discovery** (they are classified and clustered here); they are a design-phase Skip Mapping (no standalone AWS target) — see `design-refs/fast-path.md`. - `google_app_engine_flexible_app_version` — App Engine Flexible service/version (config source for the parent; same handling as the standard variant) ### Encryption (`encryption`) diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md index c5e3af21..42fe6239 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md @@ -127,11 +127,11 @@ Apply in order; first match wins: ### Example 4a: App Engine (standard Python web app, default preference) -- GCP: `google_app_engine_application` with one service (`default`) whose `google_app_engine_standard_app_version` has runtime=python39, instance_class=F2 -- Note: `runtime`/`instance_class` come from the `*_app_version` resource, not the parent. The App Engine fan-out step (`phases/design/design-infra.md`) emits one EB environment per service; `gcp_type` stays `google_app_engine_application`. +- GCP: `google_app_engine_application` with one service (`default`) whose `google_app_engine_standard_app_version` has runtime=python39, instance_class=F4, `automatic_scaling` +- Note: `runtime`/`instance_class`/scaling come from the `*_app_version` resource, not the parent. The App Engine fan-out step (`phases/design/design-infra.md`) emits one EB environment per service; `gcp_type` stays `google_app_engine_application`. - Signals: PaaS deployment, `compute_model` absent or `"managed_platform"` - Fast-path condition met: `compute_model` not set to `"container_orchestration"` or `"serverless"` -- → **AWS: Elastic Beanstalk (Python 3.9, LoadBalanced, t3.medium)** — LoadBalanced uses t3.medium+ per `elastic-beanstalk.md` Sizing Defaults +- → **AWS: Elastic Beanstalk (Python 3.9, LoadBalanced, t4g.medium, arm64)** — LoadBalanced from the version's `automatic_scaling`; t4g.medium from `instance_class` F4; Graviton default (per `elastic-beanstalk.md` Sizing Defaults, which size from the version's own config, not Q6) - Confidence: `deterministic` (App Engine → EB direct mapping, condition met) ### Example 4b: App Engine (user chose container orchestration) @@ -186,7 +186,7 @@ Deterministic (fast-path) mappings omit `rubric_applied`; inferred (rubric-based }, "aws_service": "EC2", "aws_config": { - "instance_type": "t3.medium", + "instance_type": "t4g.medium", "region": "us-east-1" }, "graviton": { diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md index b66693b2..db2ceb37 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md @@ -21,7 +21,7 @@ This is not a "Terraform required" policy — it reflects which discovery paths ## When to Use -Routing signals — these apply ONLY when `google_app_engine_application` is in the inventory (any match is sufficient): +Routing signals — these apply ONLY when `google_app_engine_application` is in the inventory **and `compute_model` is absent or `"managed_platform"`** (Q7b). If the user chose `compute_model: "container_orchestration"` or `"serverless"` (Q7b = B/C), App Engine does **not** map to EB — it routes to Fargate/Lambda via the `compute.md` rubric, and this reference is not the target (see **NOT the Right Choice** below). Routing authority lives in `compute.md` / `fast-path.md` / `design-infra.md`; this file is a supplementary config reference. When the gate holds, any of these signals is sufficient: - `google_app_engine_application` detected in Terraform (strongest PaaS-to-PaaS signal) - User answers `compute_model: "managed_platform"` in Clarify (Q7b) @@ -80,7 +80,10 @@ Detect from app source to select EB platform automatically: ## Configuration -**IaC tool:** AWS CLI (`aws elasticbeanstalk` commands). Do not use EB CLI (avoids install dependency on target machine). +**Provisioning vs deployment (two distinct steps):** + +- **Provision the environment with Terraform** — the Generate phase emits `aws_elastic_beanstalk_application` + `aws_elastic_beanstalk_environment` (with `setting` blocks), consistent with the rest of the generated stack. Terraform is the source of truth for the environment's infrastructure. Do **not** use the EB CLI (`eb`) or `aws elasticbeanstalk create-environment` to provision. +- **Deploy the app code with the AWS CLI** — the one-time migration script (`03-migrate-containers.sh`) bundles the app source (including the `Dockerfile` for the Docker platform, which EB builds at deploy time) and runs `aws elasticbeanstalk create-application-version` + `update-environment` to push it into the Terraform-provisioned environment. This is app migration, not provisioning. **Port:** On AL2023 platforms the reverse proxy (nginx) forwards to the application on port **5000** by default, and EB sets the `PORT` environment variable to that value. However, most application frameworks bind to their own default port unless told otherwise (e.g. Node/Express often 3000, Python/gunicorn 8000, .NET/Kestrel 5000/8080). **The app must listen on the port EB advertises**, or the first deploy fails its health checks. Two safe options: @@ -99,7 +102,7 @@ Migrated App Engine apps already read `PORT` (App Engine sets it too), so the fi | Immutable | Prod (safe rollback) | | TrafficSplitting | Canary/prod (percentage-based) | -**Secrets:** Use the `aws:elasticbeanstalk:application:environmentsecrets` namespace to fetch values from Secrets Manager or SSM Parameter Store into environment variables at instance bootstrap. Supported on platform versions released **on or after March 26, 2025**. (JSON-key extraction from a Secrets Manager secret — appending `:keyName` to the ARN — requires platform versions on or after January 13, 2026.) Values are pulled at bootstrap only; rotate via `UpdateEnvironment` or `RestartAppServer`. +**Secrets:** Use the `aws:elasticbeanstalk:application:environmentsecrets` namespace to fetch values from Secrets Manager or SSM Parameter Store into environment variables at instance bootstrap. Supported on platform versions released **on or after March 26, 2025**. (JSON-key extraction from a Secrets Manager secret — appending `:keyName` to the ARN — requires platform versions on or after January 13, 2026.) Values are pulled at bootstrap only; rotate via `UpdateEnvironment` or `RestartAppServer`. Source: [EB dev guide — environment properties and secrets](https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/environments-cfg-secrets.html); re-verify the namespace and cutoff dates against that page before relying on them, as EB platform schedules change. **IAM:** @@ -108,8 +111,9 @@ Migrated App Engine apps already read `PORT` (App Engine sets it too), so the fi **VPC:** -- Web tier: ALB in public subnets, EC2 instances in private subnets (instances reach the internet via NAT for outbound; only the ALB is public-facing) -- Worker tier: instances in private subnets (no public access needed) +- Web tier, **LoadBalanced**: ALB in public subnets, EC2 instances in private subnets (instances reach the internet via NAT for outbound; only the ALB is public-facing) +- Web tier, **SingleInstance** (no ALB): the single instance goes in a **public** subnet with a public IP (`AssociatePublicIpAddress=true`) so it is reachable — there is no load balancer to front it. This is the one case where an EB instance is public-facing; scope its security group tightly (only the app port from the intended sources) since it lacks the ALB boundary. Prefer LoadBalanced for anything internet-facing at scale. +- Worker-style background work is not an EB Worker tier here (see Environment Types); it maps to EventBridge + SQS alongside the web env. **Scaling:** @@ -118,90 +122,113 @@ Migrated App Engine apps already read `PORT` (App Engine sets it too), so the fi ## GCP App Engine to EB Mapping -| App Engine Feature | EB Equivalent | -| ---------------------------- | ----------------------------------------------------------------- | -| App Engine Standard | EB with matching platform (Python, Node, etc.) | -| App Engine Flexible | EB Docker platform | -| `app.yaml` env vars | EB environment properties | -| `cron.yaml` | EB worker + EventBridge scheduled events | -| Task queues | EB worker + SQS | -| `instance_class` (F/B tiers) | Right-sizing signal for instance type (see Sizing Defaults below) | -| Automatic scaling min/max | EB auto-scaling min/max instances | +| App Engine Feature | EB Equivalent | +| ---------------------------- | ------------------------------------------------------------------- | +| App Engine Standard | EB with matching platform (Python, Node, etc.) | +| App Engine Flexible | EB Docker platform | +| `app.yaml` env vars | EB environment properties | +| `cron.yaml` | EventBridge scheduled events → the web-tier env (no EB Worker tier) | +| Task queues | SQS + EventBridge → the web-tier env (no EB Worker tier) | +| `instance_class` (F/B tiers) | Right-sizing signal for instance type (see Sizing Defaults below) | +| Automatic scaling min/max | EB auto-scaling min/max instances | ### Where the config comes from (Terraform) In Terraform, `google_app_engine_application` is only the **container** for an app — it carries the project/location, not the workload config. The `runtime`, `instance_class`, `env_variables`, and scaling settings live on the **`google_app_engine_standard_app_version`** / **`google_app_engine_flexible_app_version`** resources. Each such resource is one **version** of one **service** (identified by its `service` argument; a service can have many versions). -During discovery these version resources are classified SECONDARY (`configuration`), and the App Engine fan-out step in `phases/design/design-infra.md` reads them when mapping the parent. Treat them as **config sources for the EB mapping**, not skipped resources: +During discovery these version resources are classified SECONDARY (`configuration`), and the App Engine fan-out step in `phases/design/design-infra.md` reads their config when mapping the parent. They are **config sources**: their attributes build the parent's EB mapping, but they are not emitted as standalone resources (they are a Skip Mapping — logged to `warnings`, see `fast-path.md`). Read them as follows: -- **One EB environment per App Engine _service_, not per version.** Group app_version resources by their `service` value. Multiple versions of the same service (e.g. `v1`, `v2` both with `service = "myapp"`) collapse to **one** EB environment — pick the serving/most-recent version for config (prefer `serving_status = "SERVING"`; otherwise the highest `version_id`). Distinct `service` values (e.g. `default`, `worker`) each become a **separate** EB environment under one EB application. Do not emit one env per version, and do not collapse distinct services into one. +- **One EB environment per App Engine _service_, not per version.** Group app_version resources by their `service` value (a version with no `service` argument belongs to service `"default"`). Multiple versions of the same service (e.g. `v1`, `v2` both with `service = "myapp"`) collapse to **one** EB environment — pick the config-source version by the tie-break defined in `design-infra.md` fan-out step 4 (prefer `serving_status = "SERVING"`; if several or none are SERVING, highest `version_id` by case-insensitive lexical order, with a `warnings` note). Distinct `service` values (e.g. `default`, `worker`) each become a **separate** EB environment under one EB application. Do not emit one env per version, and do not collapse distinct services into one. - Read `runtime` → EB platform (via Platform Detection Rules above). For Flexible, `flexible_runtime_settings.operating_system` / `runtime_version` may further qualify it. - Read `automatic_scaling` / `basic_scaling` / `manual_scaling` → EB min/max instances (whichever block is present; Standard defaults to automatic). - Read `env_variables` → EB environment properties. -- Derive `instance_type` from **environment type** via Sizing Defaults (below); use the Standard `instance_class` (F/B tiers) or the Flexible `resources` block (`cpu` / `memory_gb`) only to right-size within that band. +- Derive `environment_type` and `instance_type` **from the service's own config** (see Sizing Defaults below): the app_version's scaling block sets the environment type (multi-instance/autoscaled → LoadBalanced; single/manual-1 → SingleInstance) and its `instance_class` (Standard) or `resources` block (Flexible) sets the instance size. This is per-service — different services of one app can land in different bands. Default to Graviton (`t4g.*`) and emit a `graviton` block. If **only** `google_app_engine_application` is present (no app_version resources — e.g. billing-only or partial Terraform), map a single EB environment and detect the platform/runtime from the app source instead, noting the assumption in `warnings`. ## Sizing Defaults -Instance type is driven by **environment type** (which follows from the availability/uptime preference), not by a direct App Engine instance-class lookup: +EB environment type and instance size are derived **per service, from that service's own App Engine config** — the same "size from the source resource" approach the rubric uses for Cloud Run → Fargate. Q6 availability is a _database_ HA signal and does **not** drive EB compute sizing. + +**Environment type** — from the app_version's scaling block: + +| App Engine scaling on the version | EB environment type | ALB | Min instances | +| ----------------------------------------------------------------------------- | ------------------- | --- | ------------------------------------------------------------------- | +| `automatic_scaling`, or `manual_scaling`/`basic_scaling` with **>1** instance | LoadBalanced | Yes | 2 (or the version's `min_instances`/`min_idle_instances` if higher) | +| `manual_scaling`/`basic_scaling` with **1** instance, or no scaling block | SingleInstance | No | 1 | + +**Instance size** — from the version's declared class/resources, defaulting to Graviton (`t4g.*`): -| Environment | Type | Instance | ALB | Min Instances | Multi-AZ | -| ----------- | -------------- | ---------- | --- | ------------- | -------- | -| Dev | SingleInstance | t3.small | No | 1 | No | -| Prod | LoadBalanced | t3.medium+ | Yes | 2 | Yes | +| App Engine signal | EB instance type (Graviton default) | +| ------------------------------------------------------ | --------------------------------------------- | +| Standard `instance_class` F1 / B1 | t4g.small | +| Standard `instance_class` F2 / B2 | t4g.small | +| Standard `instance_class` F4 / F4_1G / B4 / B4_1G / B8 | t4g.medium | +| Flexible `resources { cpu, memory_gb }` | smallest `t4g.*` meeting cpu+memory | +| absent / unknown | t4g.small (note the assumption in `warnings`) | -**Right-sizing with `instance_class`:** App Engine instance classes have no 1:1 EC2 equivalent (App Engine sizes by memory/CPU limits, not instance families). Use the class only to nudge within the environment band above — start at the band default and step up one size for larger classes (e.g. F4/F4_1G, or B4/B8), rather than mapping each class to a specific type. When unsure, keep the band default and note the assumption in `warnings`. +**CPU architecture:** default to **Graviton (ARM64)** per the skill-wide posture — EB runs on EC2, so the same `cpu_architecture` decision applies (see `shared/graviton.md`). Use the `t4g.*` families above; fall back to x86 (`t3.*`) only when `cpu_architecture.value == "x86"` or the runtime/image is Graviton-incompatible (e.g. an App Engine Flexible → Docker image built only for amd64, or a .NET workload not published for arm64), and record the reason in the `graviton` block `caveats`. Emit a `graviton` block on each EB mapping (see `compute.md` Output Schema). ## Output Schema -The design phase emits **one mapping per App Engine service** (see the App Engine fan-out step in `phases/design/design-infra.md`). `gcp_type` stays `google_app_engine_application` (the Direct Mappings row), and `aws_config.source_service` records which App Engine service the environment came from. `runtime` and `instance_class` are read from that service's `*_app_version` resource, not from the parent; `instance_type` follows the Sizing Defaults table (LoadBalanced → t3.medium+; SingleInstance → t3.small). +The design phase emits **one mapping per App Engine service** (see the App Engine fan-out step in `phases/design/design-infra.md`). `gcp_type` stays `google_app_engine_application` (the Direct Mappings row), and `aws_config.source_service` records which App Engine service the environment came from. `runtime`, `instance_class`, and the scaling block are read from that service's `*_app_version` resource, not from the parent. `environment_type` and `instance_type` are derived **per service from that version's own scaling/class** (see Sizing Defaults) — so services can differ. Each mapping carries a `graviton` block like other compute resources. This shape lives inside the standard `aws-design.json` cluster structure — the array below shows only the two resource objects. -Example — a two-service app (`default` + `worker`) produces two mappings under one EB application: +Example — a two-service app: `default` (a web service with `automatic_scaling` → LoadBalanced) and `worker` (a `manual_scaling { instances = 1 }` service → SingleInstance). Each service's env type comes from its own scaling block: ```json [ { "gcp_type": "google_app_engine_application", - "gcp_address": "example-app", + "gcp_address": "google_app_engine_application.example#default", "gcp_config": { "source_service": "default", - "runtime": "python39", - "instance_class": "F2" + "runtime": "python312", + "instance_class": "F4", + "scaling": "automatic_scaling" }, "aws_service": "Elastic Beanstalk", "aws_config": { - "eb_application": "example-app", + "eb_application": "example", "eb_environment": "default", "source_service": "default", - "platform": "Python 3.9 running on 64bit Amazon Linux 2023", + "platform": "Python 3.12 running on 64bit Amazon Linux 2023", "environment_type": "LoadBalanced", - "instance_type": "t3.medium", + "instance_type": "t4g.medium", + "min_instances": 2, + "max_instances": 10, + "env_variables": { "LOG_LEVEL": "info" }, "region": "us-east-1" }, + "graviton": { "compatibility": "ready", "target_architecture": "arm64", "caveats": [] }, "confidence": "deterministic", - "rationale": "Direct Mapping: App Engine service 'default' → Elastic Beanstalk environment (compute_model absent or managed_platform)" + "human_expertise_required": false, + "rationale": "Direct Mapping: App Engine service 'default' → Elastic Beanstalk environment (compute_model absent or managed_platform); LoadBalanced + t4g.medium from the version's automatic_scaling and instance_class F4" }, { "gcp_type": "google_app_engine_application", - "gcp_address": "example-app", + "gcp_address": "google_app_engine_application.example#worker", "gcp_config": { "source_service": "worker", - "runtime": "python39", - "instance_class": "B2" + "runtime": "python312", + "instance_class": "B2", + "scaling": "manual_scaling(instances=1)" }, "aws_service": "Elastic Beanstalk", "aws_config": { - "eb_application": "example-app", + "eb_application": "example", "eb_environment": "worker", "source_service": "worker", - "platform": "Python 3.9 running on 64bit Amazon Linux 2023", + "platform": "Python 3.12 running on 64bit Amazon Linux 2023", "environment_type": "SingleInstance", - "instance_type": "t3.small", + "instance_type": "t4g.small", + "min_instances": 1, + "max_instances": 1, "region": "us-east-1" }, + "graviton": { "compatibility": "ready", "target_architecture": "arm64", "caveats": [] }, "confidence": "deterministic", - "rationale": "Direct Mapping: App Engine service 'worker' → Elastic Beanstalk environment (compute_model absent or managed_platform)" + "human_expertise_required": false, + "rationale": "Direct Mapping: App Engine service 'worker' → Elastic Beanstalk environment (compute_model absent or managed_platform); SingleInstance + t4g.small from the version's manual_scaling (1 instance) and instance_class B2" } ] ``` diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md index 8a36cfc7..5087cea3 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md @@ -72,30 +72,34 @@ Q12 (traffic) and Q13 (I/O) tune sizing and storage **within** the family Q6 cho These GCP resources do **not** require AWS equivalents in v1.0: -| GCP Service | Reason | -| ---------------------------- | ------------------------------------------------------------------------------------- | -| `google_project` | AWS account structure (manual, not IaC) | -| `google_monitoring_*` | Fallback to CloudWatch (managed) | -| `google_logging_*` | Fallback to CloudWatch Logs (managed) | -| `google_compute_address` | Elastic IPs managed by ALB/NAT (not standalone) | -| `google_identity_platform_*` | Auth provider — keep existing solution, do not migrate to AWS Cognito or any AWS auth | -| `google_firebase_auth_*` | Auth provider — keep existing solution, do not migrate to AWS Cognito or any AWS auth | - -**Note — App Engine app_version resources are NOT skips.** `google_app_engine_standard_app_version` and `google_app_engine_flexible_app_version` are **config sources** for the App Engine → Elastic Beanstalk mapping, not skipped resources. They carry `runtime`, `instance_class`, `env_variables`, and scaling — exactly the data EB needs — which live on the _version_ resource, not on the parent `google_app_engine_application`. Discovery classifies them SECONDARY (`configuration`); because they link to the parent only by shared `project` (no ID reference), `serves[]` may be empty and they may cluster separately, so the **App Engine fan-out** step in `phases/design/design-infra.md` locates them by scanning the full inventory and emits **one EB environment per App Engine service** (grouping multiple versions of a service into one), so a multi-service app does not collapse into a single mapping. See `elastic-beanstalk.md` → "Where the config comes from (Terraform)". +| GCP Service | Reason | +| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `google_project` | AWS account structure (manual, not IaC) | +| `google_monitoring_*` | Fallback to CloudWatch (managed) | +| `google_logging_*` | Fallback to CloudWatch Logs (managed) | +| `google_compute_address` | Elastic IPs managed by ALB/NAT (not standalone) | +| `google_identity_platform_*` | Auth provider — keep existing solution, do not migrate to AWS Cognito or any AWS auth | +| `google_firebase_auth_*` | Auth provider — keep existing solution, do not migrate to AWS Cognito or any AWS auth | +| `google_app_engine_standard_app_version` | **Config source, not a standalone target.** Carries `runtime`/`instance_class`/scaling/`env_variables` that feed the parent's App Engine → Elastic Beanstalk mapping (see note below). Skip as its own resource; log to `warnings`. | +| `google_app_engine_flexible_app_version` | **Config source, not a standalone target.** Same as the standard variant (Flexible uses `resources` + `flexible_runtime_settings`). Skip as its own resource; log to `warnings`. | + +**Note — App Engine app_version resources are config sources for the EB mapping.** `google_app_engine_standard_app_version` and `google_app_engine_flexible_app_version` are skipped as standalone resources (above), **but their config is read** to build the parent's mapping: `runtime`, `instance_class`, `env_variables`, and scaling live on the _version_ resource, not on the parent `google_app_engine_application`. Discovery classifies them SECONDARY (`configuration`); because they link to the parent only by shared `project` (no ID reference), `serves[]` may be empty and they may cluster separately, so the **App Engine fan-out** step in `phases/design/design-infra.md` locates them by scanning the full inventory and emits **one EB environment per App Engine service** (grouping multiple versions of a service into one), so a multi-service app does not collapse into a single mapping. On the non-EB paths (Q7b container-orchestration/serverless, ambiguous attribution, or no parent) they are simply skipped like any other Skip Mapping — logged to `warnings`, never sent through the rubric or the unknown-type STOP. See `elastic-beanstalk.md` → "Where the config comes from (Terraform)". ## Preferred AWS Target Services For the workload categories below, always steer toward the **preferred** AWS service. If rubric evaluation or LLM reasoning would select a non-preferred service, substitute the preferred alternative instead. -| Workload Category | Preferred AWS Target | Rationale | -| ----------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | -| Containerized workloads | Fargate (default), Lambda (event-driven), EKS (K8s required) | Deeper integration with VPC, ALB, IAM, and auto-scaling than lighter-weight alternatives | -| PaaS / managed platform | Elastic Beanstalk (when `compute_model: "managed_platform"` or App Engine source) | Preserves managed platform model; AWS manages deployments, scaling, patching, monitoring | -| Third-party auth in use | Keep existing auth provider | Startups using Auth0, Firebase Auth, Clerk, etc. should preserve their existing investment | +| Workload Category | Preferred AWS Target | Rationale | +| ----------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| Containerized workloads | Fargate (default), Lambda (event-driven), EKS (K8s required) | Deeper integration with VPC, ALB, IAM, and auto-scaling than lighter-weight alternatives | +| PaaS / managed platform | Elastic Beanstalk — **only when `compute_model` is absent or `"managed_platform"`** | Preserves managed platform model; AWS manages deployments, scaling, patching, monitoring | +| Third-party auth in use | Keep existing auth provider | Startups using Auth0, Firebase Auth, Clerk, etc. should preserve their existing investment | **Enforcement:** Check every resource mapping against this table before writing to `aws-design.json` or `aws-design-billing.json`. If a mapping would select a non-preferred service, substitute the preferred alternative. -**Precedence:** Match resources to the **first applicable row**. Cloud Run matches "Containerized workloads" regardless of `compute_model`. The "PaaS / managed platform" row applies only to App Engine resources and resources that reached Pass 2 without matching a prior row. +**Enforcement exemption (App Engine + explicit compute_model):** When the user set `compute_model: "container_orchestration"` or `"serverless"` (Q7b = B/C), the App Engine eliminator in `compute.md` intentionally routes the resource to Fargate/Lambda. **Do not** apply the PaaS row to re-substitute Elastic Beanstalk in that case — the PaaS row's condition (`compute_model` absent or `"managed_platform"`) is already false, so it does not match. This mirrors how Cloud Run is exempt (always Containerized) and Cloud SQL is exempt (Q6 governs RDS-vs-Aurora). + +**Precedence:** Match resources to the **first applicable row**. Cloud Run matches "Containerized workloads" regardless of `compute_model`. The "PaaS / managed platform" row applies to App Engine resources **only when its `compute_model` condition holds** (absent or `"managed_platform"`); an App Engine resource with `compute_model: "container_orchestration"`/`"serverless"` matches no row here and keeps its rubric-selected Fargate/Lambda target. **Exception:** For Cloud SQL PostgreSQL/MySQL, **Q6 availability always overrides** any implicit Aurora preference. Do not substitute Aurora when `availability` is `single-az` or `multi-az`. diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/index.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/index.md index ae75c466..c5c51f92 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/index.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/index.md @@ -4,13 +4,13 @@ ## Compute Services -| GCP Service | Resource Type | Reference File | Typical AWS target | -| ------------------- | -------------------------------- | -------------- | -------------------------------------- | -| Cloud Run | `google_cloud_run_service` | `compute.md` | Fargate | -| Cloud Functions | `google_cloudfunctions_function` | `compute.md` | Lambda | -| Compute Engine (VM) | `google_compute_instance` | `compute.md` | EC2 or Fargate | -| GKE | `google_container_cluster` | `compute.md` | EKS | -| App Engine | `google_app_engine_application` | `compute.md` | Elastic Beanstalk (default) or Fargate | +| GCP Service | Resource Type | Reference File | Typical AWS target | +| ------------------- | -------------------------------- | -------------- | ----------------------------------------------- | +| Cloud Run | `google_cloud_run_service` | `compute.md` | Fargate | +| Cloud Functions | `google_cloudfunctions_function` | `compute.md` | Lambda | +| Compute Engine (VM) | `google_compute_instance` | `compute.md` | EC2 or Fargate | +| GKE | `google_container_cluster` | `compute.md` | EKS | +| App Engine | `google_app_engine_application` | `compute.md` | Elastic Beanstalk (default), Fargate, or Lambda | ## Database Services diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify.md index 16b400ec..2b48d9ba 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify.md @@ -492,6 +492,7 @@ If user opts in, present Q-E1–Q-E2 (defined in **Category E — Migration Post | Growth-stage funding path | Q3 = higher spend band | Migration funding/support program review based on spend profile | | Managed platform preference | Q7b = A (managed platform) | Elastic Beanstalk for App Engine compute targets | | Container orchestration pref | Q7b = B (container orchestration) | ECS Fargate for App Engine targets (overrides default EB mapping) | +| Serverless pref | Q7b = C (serverless) | Lambda for App Engine targets (overrides default EB mapping) | | Must stay portable | Q5 = Yes multi-cloud | EKS only, no ECS Fargate | | Kubernetes-averse | Q5 = No + Q8 = Frustrated | ECS Fargate strongly recommended | | WebSocket app | Q9 = Yes | ALB WebSocket config required | diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md index bc819aac..fc1026bf 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md @@ -31,11 +31,11 @@ For each PRIMARY resource in the cluster: - **App Engine fan-out (`google_app_engine_application` only):** the parent resource carries no workload config — `runtime`, `instance_class`, `env_variables`, and scaling live on the `google_app_engine_standard_app_version` / `google_app_engine_flexible_app_version` resources. Execute these sub-steps in order: 1. **Locate the version resources.** They do **not** reference the parent by ID (App Engine allows one app per project, so the link is the shared `project`), so they are **not** reliably in the parent's cluster or `serves[]`. Scan the **entire `gcp-resource-inventory.json`** across all clusters for app_version resources — do not rely on cluster membership. - 2. **Attribute them to this parent.** Match by shared `project` when that field is present. If `project` is absent (provider-level in Terraform) or there is only one `google_app_engine_application` in the inventory, treat **all** app_version resources as belonging to it. + 2. **Attribute them to this parent.** If there is exactly **one** `google_app_engine_application` in the inventory, all app_version resources belong to it. If there are **multiple** parents, match each app_version to the parent with the same `project`; if `project` is absent on the resources (provider-level in Terraform) with multiple parents, you cannot attribute reliably — map each parent to a single EB environment, add a `warnings` entry naming the ambiguity, and skip sub-steps 4–6 for the ambiguous parents. 3. **If none were found** (billing-only or partial Terraform): emit a single EB environment, detect the platform from app source, note the assumption in `warnings`, and skip the remaining sub-steps. - 4. **Group by `service`.** Multiple versions of the same service (e.g. `v1` and `v2`, both `service = "myapp"`) are **one** service → **one** EB environment. Pick the config-source version per service: prefer `serving_status = "SERVING"`, else the highest `version_id`. - 5. **Emit one mapping per distinct `service`**, all under a single EB application named for the parent. For each: `runtime` → EB platform; scaling block (`automatic_scaling` / `basic_scaling` / `manual_scaling`) → EB min/max; `instance_type` from environment type via Sizing Defaults (use `instance_class` or the Flexible `resources` block only to right-size within the band). See `design-refs/elastic-beanstalk.md`. - 6. **Finalize each mapping:** keep `gcp_type: "google_app_engine_application"`, `confidence: "deterministic"`, and record the source `service` in `aws_config`. Mark the consumed app_version resources as mapped so Pass 2 does not re-process them as standalone SECONDARY resources. + 4. **Group by `service`.** An app_version with no `service` argument belongs to service `"default"` (App Engine semantics). Multiple versions of the same service (e.g. `v1` and `v2`, both `service = "myapp"`) are **one** service → **one** EB environment. Pick the config-source version per service: prefer the version with `serving_status = "SERVING"`; if several (or none) are SERVING, pick the highest `version_id` by case-insensitive lexical order, and add a `warnings` entry noting the tie-break so the choice is reproducible. + 5. **Emit one mapping per distinct `service`**, all under a single EB application named for the parent. Give each mapping a **unique `gcp_address`** of the form `#` — where `` is the parent's Terraform address (e.g. `google_app_engine_application.example#default`, `google_app_engine_application.example#worker`) — so per-service mappings don't collide; keep `gcp_type: "google_app_engine_application"`. These per-service mappings **replace** the parent's single mapping — do **not** also emit a bare-parent-address mapping (no `#service` suffix); the parent is represented only by its per-service environments, so the output has exactly one resource per App Engine service, not N+1. Derive each field **from that service's own version config** (see `design-refs/elastic-beanstalk.md` Sizing Defaults): `runtime` → EB platform; scaling block (`automatic_scaling` / `basic_scaling` / `manual_scaling`) → EB `environment_type` (multi-instance/autoscaled → LoadBalanced, single/manual-1 → SingleInstance) and **both** `min_instances` and `max_instances` so Generate can emit ASG MinSize/MaxSize. Read the min/max from the correct provider field for the scaling block present: Standard `automatic_scaling.standard_scheduler_settings.{min_instances,max_instances}`; Standard `basic_scaling.max_instances` (min 1); Standard/Flexible `manual_scaling.instances` (min = max = that count); Flexible `automatic_scaling.{min_total_instances,max_total_instances}`. Default max to min when the block declares no maximum. `instance_class` (Standard) or `resources` (Flexible) → `instance_type` (Graviton `t4g.*` default); `env_variables` → carry into `aws_config.env_variables` so Generate can emit the `aws:elasticbeanstalk:application:environment` settings. Do **not** use Q6 availability for EB sizing — that governs databases only. + 6. **Finalize.** Each emitted per-service mapping keeps `confidence: "deterministic"`, `human_expertise_required: false`, a non-empty `rationale` (e.g. `"Direct Mapping: App Engine service → Elastic Beanstalk environment"`), and records the source `service` in `aws_config`. The `*_app_version` resources themselves are Skip Mappings — **not** emitted as separate output resources (config sources, no standalone cost). This step **owns their `warnings[]` entry**: add one per consumed version naming the version and the EB environment it fed (see `fast-path.md` → Skip Mappings + Secondary Behavior Lookups). Step 3 handles only versions this step did **not** consume, so there is no double-warning. 5. If `gcp_type` is `google_sql_database_instance` with PostgreSQL or MySQL engine: **always proceed to Pass 2** (Cloud SQL is not in Direct Mappings — see `fast-path.md`). Confidence = **`inferred`** after rubric. 6. If not found (or conditions not met): proceed to Pass 2 (confidence will be **`inferred`** after rubric, or **`billing_inferred`** on the billing-only path). @@ -92,12 +92,13 @@ If rubric or fast-path would select Aurora when `availability` is `single-az` or 1. **Set `human_expertise_required`**: If the BigQuery specialist gate applied, already `true`. Otherwise set `false` unless another rubric explicitly requires it. This field is REQUIRED on every resource in the output. -1. **Preferred AWS target check**: **Skip** if `aws_service` is **`Deferred — specialist engagement`**. **Skip Aurora substitution** for Cloud SQL when Q6 availability is `single-az` or `multi-az` (RDS is correct). Otherwise verify the selected `aws_service` aligns with the Preferred AWS Target Services table in `design-refs/fast-path.md`. If a non-preferred service is selected (e.g., App Runner for containerized workloads), substitute the preferred alternative (e.g., Fargate). Add a note to the rationale: "Preferred target: [alternative] selected for stronger ecosystem integration." +1. **Preferred AWS target check**: **Skip** if `aws_service` is **`Deferred — specialist engagement`**. **Skip Aurora substitution** for Cloud SQL when Q6 availability is `single-az` or `multi-az` (RDS is correct). **Skip EB substitution** for App Engine when `compute_model` is `"container_orchestration"` or `"serverless"` (the eliminator's Fargate/Lambda target is correct — the PaaS row's condition is false, see `fast-path.md` enforcement exemption). Otherwise verify the selected `aws_service` aligns with the Preferred AWS Target Services table in `design-refs/fast-path.md`. If a non-preferred service is selected (e.g., App Runner for containerized workloads), substitute the preferred alternative (e.g., Fargate). Add a note to the rationale: "Preferred target: [alternative] selected for stronger ecosystem integration." ## Step 3: Handle Secondary Resources For each SECONDARY resource: +0. **App Engine version resources** (`google_app_engine_standard_app_version` / `google_app_engine_flexible_app_version`): these are **Skip Mappings** — config sources for the App Engine → EB mapping with no standalone AWS target (see `fast-path.md`). If the App Engine fan-out (Pass 1 step 4) already consumed this version, it has already logged the `warnings[]` entry — do nothing here. Otherwise (the fan-out did not run — `container_orchestration`/`serverless` path, ambiguous attribution, or no parent), skip it now: **do not** emit it to `aws-design.json`, **do not** send it through `index.md`/rubric, and add one `warnings[]` entry for it. Either way it never reaches the Pass 2 unknown-type catch-all — so no path STOPs on it, and each version is warned exactly once. 1. Use `design-refs/index.md` for category 2. Apply fast-path (most secondaries have deterministic mappings) 3. If rubric needed: apply the **BigQuery specialist gate** (Pass 2 step 0) first when `gcp_type` starts with `google_bigquery_`; otherwise apply the same 6-criteria approach as Pass 2 diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/discover/discover-iac.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/discover/discover-iac.md index 0ff8861f..c1f719cf 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/discover/discover-iac.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/discover/discover-iac.md @@ -46,6 +46,7 @@ Sensitive key patterns to redact (case-insensitive): `password`, `passwd`, `secr - `type` (e.g., `google_compute_instance`) - `name` (resource name component, e.g., `web`) - `config` (object with key attributes: `machine_type`, `name`, `region`, etc.) + - **For `google_app_engine_standard_app_version` / `google_app_engine_flexible_app_version`**, the App Engine → Elastic Beanstalk mapping reads these attributes — capture them into `config` when present: `service`, `version_id`, `runtime`, `instance_class`, `serving_status`, `project`, the scaling block that is present (`automatic_scaling` / `basic_scaling` / `manual_scaling`), `env_variables`, and (Flexible only) `resources` and `flexible_runtime_settings`. - `raw_hcl` (raw HCL text for this resource, needed for Step 4) - `depends_on` (array of addresses this resource depends on) 4. Also extract provider and backend configuration (for region detection) diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/estimate/estimate-infra.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/estimate/estimate-infra.md index 7b88c5bb..239d7cbb 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/estimate/estimate-infra.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/estimate/estimate-infra.md @@ -111,17 +111,18 @@ Calculate 3 cost tiers to show the optimization range: **Per-service calculation approach:** -| Domain | Formula | Key inputs from aws-design.json | -| -------------------------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | -| Compute (Fargate) | (vCPU × vCPU rate + memory GB × memory rate) × 730 hours × instance count | `aws_config.cpu`, `aws_config.memory` | -| Compute (Lambda) | requests × request rate + (requests × duration × memory GB) × GB-second rate | Estimated from usage patterns | -| Database (Aurora) | instance rate × 730 hours × instance count + storage GB × storage rate + I/O estimate | `aws_config.instance_class`, `aws_config.allocated_storage` | -| Database (RDS) | instance rate × 730 hours × instance count + storage GB × storage rate | `aws_config.instance_class`, `aws_config.allocated_storage` | -| Storage (S3) | GB × per-GB rate + request estimates | `aws_config.storage_gb` or source `gcp_config` | -| Networking (ALB) | fixed monthly + LCU estimate | From compute service count | -| Networking (NAT) | fixed monthly × count + GB processed × data rate | From VPC design | -| Security (Secrets Manager) | secrets_count × per-secret monthly rate + api_calls_10k × per-10K API rate | `aws_config.secrets_count`, `aws_config.api_calls_10k` (or inferred defaults) | -| Supporting | Per-unit rates × quantities (secrets, log GB, metrics) | Inferred from service count | +| Domain | Formula | Key inputs from aws-design.json | +| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | +| Compute (Fargate) | (vCPU × vCPU rate + memory GB × memory rate) × 730 hours × instance count | `aws_config.cpu`, `aws_config.memory` | +| Compute (Lambda) | requests × request rate + (requests × duration × memory GB) × GB-second rate | Estimated from usage patterns | +| Compute (Elastic Beanstalk) | EC2 rate for `instance_type` (EC2 pricing rows) × 730 hours × `min_instances` + ALB fixed monthly + LCU estimate **when `environment_type == "LoadBalanced"`** (SingleInstance has no ALB). EB service fee is $0. One line item per EB environment (per App Engine service). | `aws_config.instance_type`, `aws_config.min_instances`, `aws_config.environment_type` | +| Database (Aurora) | instance rate × 730 hours × instance count + storage GB × storage rate + I/O estimate | `aws_config.instance_class`, `aws_config.allocated_storage` | +| Database (RDS) | instance rate × 730 hours × instance count + storage GB × storage rate | `aws_config.instance_class`, `aws_config.allocated_storage` | +| Storage (S3) | GB × per-GB rate + request estimates | `aws_config.storage_gb` or source `gcp_config` | +| Networking (ALB) | fixed monthly + LCU estimate. **Exclude LoadBalanced Elastic Beanstalk environments** — their ALB is already priced in the EB compute row; do not also count them here (EB manages its own LB, with no standalone `aws_lb` resource). | From compute service count, minus LoadBalanced EB environments | +| Networking (NAT) | fixed monthly × count + GB processed × data rate | From VPC design | +| Security (Secrets Manager) | secrets_count × per-secret monthly rate + api_calls_10k × per-10K API rate | `aws_config.secrets_count`, `aws_config.api_calls_10k` (or inferred defaults) | +| Supporting | Per-unit rates × quantities (secrets, log GB, metrics) | Inferred from service count | Show calculation breakdown per service: rate × quantity = cost. Present all 3 tiers side-by-side. @@ -156,6 +157,7 @@ Use the per-service heuristic table to estimate monthly log volume: | AWS Service (from aws-design.json) | Estimated log volume/month | Basis | | ---------------------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | Fargate task | 3 GB per task | Container stdout/stderr, request logs | +| Elastic Beanstalk environment | 3 GB per environment | App stdout/stderr + EB platform/deploy logs (one per App Engine service) | | Lambda function | 0.2 GB per function | Invocation logs at INFO level | | RDS/Aurora instance | 1 GB per instance | Error log + slow query (audit log off) | | RDS/Aurora instance (audit on) | 8 GB per instance | If source has `pgaudit.log`, `log_statement = 'all'`, `cloudsql.log_min_duration_statement`, or explicit audit/general log database flags in Terraform | @@ -286,7 +288,7 @@ _Run only when_ `preferences.json` → `design_constraints.cpu_architecture.valu Model **only the hourly price discount** — never the performance uplift. For the Balanced tier: -1. Sum the Balanced-tier monthly cost of all Graviton-targeted compute (EC2, Fargate, Lambda, and Graviton-family managed services) using `pricing-cache.md` Graviton rows. For any family/size not cached, use the EC2 (Graviton/x86) recipe above. This is `graviton_monthly` (the compute portion). +1. Sum the Balanced-tier monthly cost of all Graviton-targeted compute (EC2, Fargate, Lambda, Elastic Beanstalk EC2 instances, and Graviton-family managed services) using `pricing-cache.md` Graviton rows. For any family/size not cached, use the EC2 (Graviton/x86) recipe above. This is `graviton_monthly` (the compute portion). 2. Re-price the **same mapping** on the x86 equivalents (use the `x86 equivalent` column in `pricing-cache.md § EC2`, x86 Fargate/Lambda rates, and the x86 managed-service families). This is `x86_equivalent_monthly`. 3. Compute `savings_amount = x86_equivalent_monthly − graviton_monthly` and `savings_percent = savings_amount / x86_equivalent_monthly × 100` (one decimal). diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/generate/generate-artifacts-infra.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/generate/generate-artifacts-infra.md index 15827692..8a609567 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/generate/generate-artifacts-infra.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/generate/generate-artifacts-infra.md @@ -35,7 +35,7 @@ Generate `$MIGRATION_DIR/terraform/` with only the files needed for domains that | `security.tf` | security | IAM roles, policies, KMS keys, Secrets Manager | | `storage.tf` | storage | S3 buckets, EFS, backup vaults | | `database.tf` | database | RDS/Aurora instances, parameter groups | -| `compute.tf` | compute | Fargate/ECS, Lambda, EC2 | +| `compute.tf` | compute | Fargate/ECS, Lambda, EC2, Elastic Beanstalk | | `monitoring.tf` | monitoring | CloudWatch dashboards, alarms, log groups | | `README.md` | core | Cost tiers vs this Terraform (one stack; Balanced-aligned) | @@ -50,7 +50,7 @@ Build a generation manifest: read all resources from `aws-design.json` clusters, | IAM Role, IAM Policy, KMS Key, Secrets Manager | `security.tf` | | S3, EFS, Backup Vault | `storage.tf` | | RDS, Aurora, DynamoDB, ElastiCache | `database.tf` | -| Fargate, ECS, Lambda, EC2 | `compute.tf` | +| Fargate, ECS, Lambda, EC2, Elastic Beanstalk | `compute.tf` | | CloudWatch, SNS (for alarms) | `monitoring.tf` | > `baseline.tf` is always emitted. It is NOT driven by `aws-design.json` clusters — the resources are workload-independent account controls. The compliance-conditional subset (Config + Security Hub) is emitted within the same file when `preferences.json.compliance` contains soc2/pci/hipaa/fedramp. The `aws_budgets_budget` resource reads `estimation-infra.json` to set its `limit_amount`. See Step 1.5 below. Users who want to skip the baseline can delete `terraform/baseline.tf` before `terraform apply`. @@ -319,8 +319,9 @@ For each domain with resources in the generation manifest: `graviton.target_architecture` from `aws-design.json` (see `references/shared/graviton.md`). When `arm64`: emit `aws_ecs_task_definition` with `runtime_platform { cpu_architecture = "ARM64" operating_system_family = "LINUX" }`, - `aws_lambda_function` with `architectures = ["arm64"]`, and the Graviton instance type from - `aws_config` (e.g., `m7g.xlarge`) on EC2/EKS launch templates — add inline comment + `aws_lambda_function` with `architectures = ["arm64"]`, the Graviton instance type from + `aws_config` (e.g., `m7g.xlarge`) on EC2/EKS launch templates, and for Elastic Beanstalk the + `t4g.*` `InstanceType` setting on `aws_elastic_beanstalk_environment` — add inline comment `# Graviton (ARM64) — ~15-20% cheaper than x86; build images with --platform linux/arm64`. When `x86_64`: emit x86 types with a comment citing the blocker from `graviton.caveats`. EKS arm64 node groups are single-arch on dev tier. @@ -329,14 +330,14 @@ For each domain with resources in the generation manifest: only the gcp-to-aws resource wiring / value population per domain; for every security rule, follow the posture the skill returned in Step 3.0): -| Domain | gcp-to-aws wiring (skill owns the posture) | -| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Networking | Emit the VPC/subnets/NAT/SGs and (for compliance) flow logs per the skill's posture; populate CIDRs/AZ count from design. Wire an internet-facing ALB only if the design has one. | -| Security | Emit per-service Fargate/Lambda IAM roles and Secrets Manager resources per the skill's posture (least-privilege, no plaintext master password, compliance-conditional rotation/KMS). Populate ARNs/role names from the cluster's `secondary_resources`. | -| Storage | Emit S3 buckets per the skill's posture (versioning, SSE, block-public-access, CloudFront/OAC for public, compliance-conditional access logging). Populate bucket names/lifecycle from design. | -| Database | Emit RDS/Aurora per the skill's posture (private, encrypted, `deletion_protection`, master-password-via-Secrets-Manager, DB-port SG scoping). Populate engine/version/instance class from `aws_config`; add the Cloud SQL `authorized_networks` warning (Step 3.1) when applicable. | -| Compute | Emit Fargate/EKS/ECR per the skill's posture (private subnets, EKS private endpoint, ECR scan-on-push). Populate task CPU/memory and autoscaling from `aws_config`. Apply Graviton/ARM64 wiring from the CPU architecture rule above. | -| Monitoring | Emit CloudWatch log groups/dashboard/alarms per the skill's monitoring baseline; source alarm thresholds from `generation-infra.json` success_metrics. | +| Domain | gcp-to-aws wiring (skill owns the posture) | +| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Networking | Emit the VPC/subnets/NAT/SGs and (for compliance) flow logs per the skill's posture; populate CIDRs/AZ count from design. Wire an internet-facing ALB only if the design has one. | +| Security | Emit per-service Fargate/Lambda IAM roles and Secrets Manager resources per the skill's posture (least-privilege, no plaintext master password, compliance-conditional rotation/KMS). Populate ARNs/role names from the cluster's `secondary_resources`. | +| Storage | Emit S3 buckets per the skill's posture (versioning, SSE, block-public-access, CloudFront/OAC for public, compliance-conditional access logging). Populate bucket names/lifecycle from design. | +| Database | Emit RDS/Aurora per the skill's posture (private, encrypted, `deletion_protection`, master-password-via-Secrets-Manager, DB-port SG scoping). Populate engine/version/instance class from `aws_config`; add the Cloud SQL `authorized_networks` warning (Step 3.1) when applicable. | +| Compute | Emit Fargate/EKS/ECR per the skill's posture (private subnets, EKS private endpoint, ECR scan-on-push). Populate task CPU/memory and autoscaling from `aws_config`. Apply Graviton/ARM64 wiring from the CPU architecture rule above. **Elastic Beanstalk** (App Engine → EB): emit one `aws_elastic_beanstalk_application` for the app and one `aws_elastic_beanstalk_environment` per `source_service` (from `aws_config`). Resolve `solution_stack_name` with a **Terraform `data` source** (self-updating, needs no generate-time credentials, cannot go stale in the committed file) — do not paste `aws_config.platform` verbatim (it is a human-readable label like `"Python 3.12 running on 64bit Amazon Linux 2023"`): emit `data "aws_elastic_beanstalk_solution_stack" "" { most_recent = true, name_regex = "64bit Amazon Linux 2023 (.*) running Python 3.12" }` (build `name_regex` from the language + version in `platform`) and reference `.name`. Fallback only if a `data` source can't be used: `aws elasticbeanstalk list-available-solution-stacks` at generate time. Also emit the EB **instance profile** this environment references — `aws_iam_role` (EC2 trust) + `aws_iam_instance_profile` + the managed-policy attachment (`AWSElasticBeanstalkWebTier`, plus `AWSElasticBeanstalkWorkerTier`/`AWSElasticBeanstalkMulticontainerDocker` as the platform needs) — since EB does not auto-create it in Terraform; do not reference an instance profile that no resource emits. Emit `setting` blocks for: `aws:autoscaling:launchconfiguration/IamInstanceProfile` (the instance profile just emitted); `aws:autoscaling:launchconfiguration/SecurityGroups` (an SG for the instances, required when they sit in private subnets); `aws:autoscaling:launchconfiguration/InstanceType` (from `instance_type`); `aws:elasticbeanstalk:environment/EnvironmentType` (from `environment_type`); **when `environment_type == "LoadBalanced"`, also `aws:elasticbeanstalk:environment/LoadBalancerType = application`** (ALB, not the Classic default) plus `aws:autoscaling:asg/MinSize` (from `min_instances`) and `MaxSize` (from `max_instances`); `aws:ec2:vpc/VPCId`, `Subnets`, and `ELBSubnets` — for **LoadBalanced**, instances in private subnets + ALB in public (`ELBScheme` public); for **SingleInstance** (no ALB) put the instance in a **public** subnet with `AssociatePublicIpAddress=true` so it is reachable; and `aws:elasticbeanstalk:application:environment` for env vars. ARM64: pick the `t4g.*` instance type from `aws_config` per the Graviton rule. See `design-refs/elastic-beanstalk.md`. | +| Monitoring | Emit CloudWatch log groups/dashboard/alarms per the skill's monitoring baseline; source alarm thresholds from `generation-infra.json` success_metrics. | ## Step 4: Generate outputs.tf diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/generate/generate-artifacts-scripts.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/generate/generate-artifacts-scripts.md index 6447920c..79e35ae2 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/generate/generate-artifacts-scripts.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/generate/generate-artifacts-scripts.md @@ -31,7 +31,7 @@ Set boolean flags for downstream script generation: "ElastiCache", "Redshift" OR `gcp_type` starting with `google_sql_`, `google_firestore_`, `google_bigtable_`, `google_bigquery_`, `google_redis_` - **has_storage**: true if ANY resource has `aws_service` = "S3" OR `gcp_type` = `google_storage_bucket` -- **has_containers**: true if ANY resource has `aws_service` containing "Fargate", "ECS", "EKS" +- **has_containers**: true if ANY resource has `aws_service` containing "Fargate", "ECS", "EKS", or "Elastic Beanstalk" OR `gcp_type` starting with `google_cloud_run_`, `google_container_cluster` - **has_secrets**: true if ANY resource has `aws_service` containing "Secrets Manager" OR `gcp_type` starting with `google_secret_manager_` @@ -221,13 +221,19 @@ echo "TODO: Compare row counts between source and target" **Skip this script entirely if `has_containers` is false.** -Migrate container images from GCR/Artifact Registry to ECR: +This script has two independent sections; emit each only if its services are present in `aws-design.json`: + +- **Shared preamble** — **always emit** (shebang, `DRY_RUN`, account/region). Both sections depend on it, so it must not live inside either one. +- **ECR image migration** — emit if any resource maps to Fargate/ECS/EKS (container images move GCR/Artifact Registry → ECR). +- **Elastic Beanstalk source deploy** — emit if any resource maps to Elastic Beanstalk. **EB does not take a pre-built image from a registry:** for the Docker platform you ship a **source bundle containing the `Dockerfile`** (plus a `Dockerrun.aws.json` only for the pre-built-image case, which this skill does not use) and EB builds the image during deployment; for the language platforms (Python/Node/etc.) you ship the app source. So there is **no ECR push for EB** — do not create an ECR repo for an EB service. The EB section bundles the app source, uploads it to the EB-managed S3 bucket, creates an application version, and deploys it to the environment Terraform provisioned. + +Shared preamble (always emitted): ```bash #!/usr/bin/env bash set -euo pipefail -# Container image migration: GCR → ECR +# App migration: container images (→ ECR) and/or Elastic Beanstalk source deploy # Usage: ./03-migrate-containers.sh [--execute] DRY_RUN=true @@ -235,6 +241,11 @@ DRY_RUN=true AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) AWS_REGION="us-east-1" # From preferences.json target_region +``` + +ECR image migration — GCR/Artifact Registry → ECR (**include ONLY if Fargate/ECS/EKS present**): + +```bash ECR_REGISTRY="${AWS_ACCOUNT_ID}.dkr.ecr.${AWS_REGION}.amazonaws.com" # TODO: List container images from aws-design.json compute resources @@ -264,11 +275,49 @@ for IMAGE in "${IMAGES[@]}"; do docker push "$ECR_REGISTRY/$IMAGE_NAME:$IMAGE_TAG" fi done +``` -# Verification +Elastic Beanstalk source deploy (**include ONLY if an EB environment is in `aws-design.json`**): + +```bash +# EB builds the image from your Dockerfile at deploy time (Docker platform) or runs the app +# source directly (language platforms). No ECR push. One deploy per App Engine service → EB env. +# TODO: one entry per EB environment from aws-design.json (eb_application, eb_environment, source_service) +EB_APP="gcp-migration" # aws_config.eb_application +EB_ENVS=( + "default" # aws_config.eb_environment (one per source_service) + # "worker" +) +EB_BUCKET="elasticbeanstalk-${AWS_REGION}-${AWS_ACCOUNT_ID}" + +for EB_ENV in "${EB_ENVS[@]}"; do + VERSION_LABEL="${EB_ENV}-$(date -u +%Y%m%d%H%M%S)" # stamp at run time + BUNDLE="/tmp/${EB_APP}-${EB_ENV}.zip" + if [ "$DRY_RUN" = true ]; then + echo "[DRY RUN] Would bundle ./${EB_ENV} (incl. Dockerfile), upload to s3://${EB_BUCKET}, and deploy version ${VERSION_LABEL} to ${EB_APP}/${EB_ENV}" + else + echo "Bundling source for ${EB_ENV} (must include Dockerfile for the Docker platform)..." + ( cd "./${EB_ENV}" && zip -r "$BUNDLE" . -x '*.git*' ) + aws s3 cp "$BUNDLE" "s3://${EB_BUCKET}/${VERSION_LABEL}.zip" --region "$AWS_REGION" + aws elasticbeanstalk create-application-version --application-name "$EB_APP" \ + --version-label "$VERSION_LABEL" \ + --source-bundle "S3Bucket=${EB_BUCKET},S3Key=${VERSION_LABEL}.zip" --region "$AWS_REGION" + aws elasticbeanstalk update-environment --application-name "$EB_APP" \ + --environment-name "$EB_ENV" --version-label "$VERSION_LABEL" --region "$AWS_REGION" + fi +done +``` + +Verification (always emitted; each line only reports on the sections you included): + +```bash echo "=== Verification ===" -echo "Listing ECR repositories..." -aws ecr describe-repositories --region "$AWS_REGION" --query 'repositories[].repositoryName' --output table +# Include the next two lines only if the ECR section was emitted: +echo "Listing ECR repositories (Fargate/ECS/EKS)..." +aws ecr describe-repositories --region "$AWS_REGION" --query 'repositories[].repositoryName' --output table 2>/dev/null || echo "No ECR repositories (expected if EB-only)" +# Include the next two lines only if the EB section was emitted: +echo "Listing Elastic Beanstalk environments..." +aws elasticbeanstalk describe-environments --region "$AWS_REGION" --query 'Environments[].{Env:EnvironmentName,Status:Status,Health:Health}' --output table 2>/dev/null || echo "No EB environments" ``` ### 04-migrate-secrets.sh — IF has_secrets @@ -346,11 +395,15 @@ cd terraform/ terraform state list | wc -l echo "resources in Terraform state" -# --- Include ONLY if has_containers --- +# --- Include ONLY if Fargate/ECS/EKS present --- # Check ECS services echo "--- ECS Services ---" aws ecs list-services --cluster "${PROJECT_NAME:-gcp-migration}" --query 'serviceArns' --output table 2>/dev/null || echo "No ECS cluster found" +# --- Include ONLY if Elastic Beanstalk present --- +echo "--- Elastic Beanstalk Environments ---" +aws elasticbeanstalk describe-environments --query 'Environments[].{Env:EnvironmentName,Status:Status,Health:Health}' --output table 2>/dev/null || echo "No EB environments found" + # --- Include ONLY if has_databases --- # Check RDS instances echo "--- RDS Instances ---" diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/shared/pricing-cache.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/shared/pricing-cache.md index 8db3a919..b3cc79db 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/shared/pricing-cache.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/shared/pricing-cache.md @@ -44,12 +44,14 @@ arm64 (Graviton) is ~20% below x86 per GB-second; per-request price is the same Costs are the underlying resources (EC2, ALB, EBS, CloudWatch). Typical estimates: -| Profile | Underlying Resources | Estimated Monthly | -| --------------------------------------------- | -------------------------------- | ----------------- | -| Dev (single instance, t3.small, no ALB) | EC2 t3.small | ~$15 | -| Dev + RDS (t3.small + db.t4g.micro) | EC2 + RDS db.t4g.micro | ~$30–50 | -| Prod (load-balanced, 2× t3.medium, ALB) | 2× EC2 + ALB + EBS | ~$95–120 | -| Prod + RDS (2× t3.medium, ALB, db.t4g.medium) | 2× EC2 + ALB + RDS db.t4g.medium | ~$175–220 | +Profiles below use the Graviton (`t4g.*`) default that the design phase now emits for EB; for x86 environments use the `t3.*` equivalents (~15–20% higher). + +| Profile | Underlying Resources | Estimated Monthly | +| ---------------------------------------------- | -------------------------------- | ----------------- | +| Dev (single instance, t4g.small, no ALB) | EC2 t4g.small | ~$12 | +| Dev + RDS (t4g.small + db.t4g.micro) | EC2 + RDS db.t4g.micro | ~$28–48 | +| Prod (load-balanced, 2× t4g.medium, ALB) | 2× EC2 + ALB + EBS | ~$85–110 | +| Prod + RDS (2× t4g.medium, ALB, db.t4g.medium) | 2× EC2 + ALB + RDS db.t4g.medium | ~$165–210 | ### EKS diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/shared/schema-discover-iac.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/shared/schema-discover-iac.md index fd352af6..fcee0a1b 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/shared/schema-discover-iac.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/shared/schema-discover-iac.md @@ -55,6 +55,29 @@ Complete inventory of discovered GCP resources with classification, dependencies "depth": 2, "cluster_id": "compute_cloudrun_us-central1_001" }, + { + "address": "google_app_engine_standard_app_version.default_v1", + "type": "google_app_engine_standard_app_version", + "name": "default_v1", + "classification": "SECONDARY", + "tier": "compute", + "confidence": 0.99, + "secondary_role": "configuration", + "serves": [], + "config": { + "service": "default", + "version_id": "v1", + "runtime": "python312", + "instance_class": "F2", + "serving_status": "SERVING", + "project": "my-gcp-project", + "automatic_scaling": { + "standard_scheduler_settings": { "min_instances": 2, "max_instances": 10 } + } + }, + "depth": 1, + "cluster_id": "compute_appengine_us-central1_001" + }, { "address": "google_compute_network.main", "type": "google_compute_network", From 1e2570435c5c212b8be1cb54a9893574e17c8c85 Mon Sep 17 00:00:00 2001 From: Paul Pollack Date: Fri, 24 Jul 2026 16:49:19 -0400 Subject: [PATCH 7/8] =?UTF-8?q?fix(gcp-to-aws):=20Q7b=20respects=20Q5=3Dmu?= =?UTF-8?q?lti-cloud=20=E2=80=94=20App=20Engine=20=E2=86=92=20EKS=20overri?= =?UTF-8?q?de?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When Q5 = multi-cloud portability, Q5 already records `compute: "eks"`, but Q7b still defaulted App Engine → Elastic Beanstalk (the most AWS-locked-in target). The two signals were never connected — same conflicting-signal class as the earlier GKE/Q8 issue. Mirror Q8's existing multi-cloud early-exit for Q7b, end to end: Clarify: - Q7b fires only when App Engine present AND Q5 != multi-cloud; N/A otherwise. - Early-Exit + sheet-correction rules route App Engine → EKS under multi-cloud, overriding the EB default. Disposition catalog, defaults table, and answer- combination triggers updated to match. Design: - fast-path Direct Mapping + PaaS Preferred-Target rows gated on `compute` != "eks"; new enforcement exemption mirrors the compute_model one. - compute.md rubric: `compute: "eks"` is a criterion-1 hard override (selects EKS and stops before the managed-platform/EB branch — fixes first-match-wins ordering that would otherwise pick EB/Fargate first). Example 4c added. - design-infra Pass 1 condition, App Engine fan-out suppression, and Preferred- target check updated; app_version resources skipped like any non-EB path (no pipeline STOP, no double-warning). - design-billing path: App Engine → EKS override applies in billing-only mode too (compute:eks is a top-level constraint, not a config detail). - index.md / elastic-beanstalk.md / SKILL.md load-conditions updated. Verified: fmt:check, lint:md, lint:types, test, lint:frontmatter, shared:check, fixtures:check, fixtures:assert, gitleaks all green. Adversarial review pass confirmed routing is STOP-safe and key discipline (compute vs compute_model vs kubernetes) is clean. --- .../skills/gcp-to-aws/SKILL.md | 22 ++-- .../references/design-refs/compute.md | 38 ++++-- .../design-refs/elastic-beanstalk.md | 6 +- .../references/design-refs/fast-path.md | 48 +++---- .../references/design-refs/index.md | 14 +- .../phases/clarify/clarify-compute.md | 4 +- .../references/phases/clarify/clarify.md | 120 +++++++++--------- .../phases/design/design-billing.md | 4 +- .../references/phases/design/design-infra.md | 8 +- 9 files changed, 141 insertions(+), 123 deletions(-) diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md index 05c280d3..a6b7e58a 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/SKILL.md @@ -33,17 +33,17 @@ Each phase loads reference files on demand. To keep per-turn context manageable **Conditional reference files (load ONLY when condition is true):** -| File | Condition | -| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `design-refs/ai-gemini-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"gemini"` or `"both"` | -| `design-refs/ai-openai-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"openai"` or `"both"` | -| `design-refs/ai-anthropic-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"anthropic"` | -| `design-refs/ai.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"other"` | -| `design-refs/elastic-beanstalk.md` | `google_app_engine_application` in inventory (optionally with `compute_model == "managed_platform"` in preferences). Supplementary reference for EB configuration detail (platforms, IAM, VPC, deployment policies). Does not replace `compute.md` — both may be needed in mixed projects. Skip when no App Engine is in inventory, even if `compute_model` is set. | -| `design-refs/design-ref-harness.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "harness"` | -| `design-refs/design-ref-agentic-to-agentcore.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "strands"` | -| `shared/retarget-gotchas.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "retarget"` | -| `shared/graviton.md` | Compute, DB, or cache in inventory OR `graviton_profile` present (Design/Estimate/Generate) | +| File | Condition | +| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `design-refs/ai-gemini-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"gemini"` or `"both"` | +| `design-refs/ai-openai-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"openai"` or `"both"` | +| `design-refs/ai-anthropic-to-bedrock.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"anthropic"` | +| `design-refs/ai.md` | `ai-workload-profile.json` exists AND `summary.ai_source` = `"other"` | +| `design-refs/elastic-beanstalk.md` | `google_app_engine_application` in inventory (optionally with `compute_model == "managed_platform"` in preferences) **and `compute` ≠ `"eks"`**. Supplementary reference for EB configuration detail (platforms, IAM, VPC, deployment policies). Does not replace `compute.md` — both may be needed in mixed projects. Skip when no App Engine is in inventory (even if `compute_model` is set), or when `compute: "eks"` (Q5 = multi-cloud) routed App Engine to EKS. | +| `design-refs/design-ref-harness.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "harness"` | +| `design-refs/design-ref-agentic-to-agentcore.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "strands"` | +| `shared/retarget-gotchas.md` | `agentic_profile.is_agentic == true` AND `ai_constraints.agentic.migration_approach == "retarget"` | +| `shared/graviton.md` | Compute, DB, or cache in inventory OR `graviton_profile` present (Design/Estimate/Generate) | When adding new reference files, verify the phase's total loaded instructions remain under budget. If a new file would exceed ~800 lines when combined with other loaded refs, split it or make it conditional. diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md index 42fe6239..1a466b95 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/compute.md @@ -5,20 +5,20 @@ **Table lookup first:** Check `fast-path.md` **Direct Mappings** for this Terraform type. - `google_cloud_run_service`, `google_cloud_run_v2_service`, `google_cloudfunctions_function`, and `google_cloudfunctions2_function` are currently in Direct Mappings and usually resolve with `confidence: "deterministic"` when row conditions are met. -- `google_app_engine_application` is now in Direct Mappings (→ Elastic Beanstalk, confidence: `deterministic`). +- `google_app_engine_application` is now in Direct Mappings (→ Elastic Beanstalk, confidence: `deterministic`) **only when `compute_model` is absent/`"managed_platform"` and `compute` ≠ `"eks"`**; under `compute: "eks"` (Q5 = multi-cloud) it falls through to the rubric and routes to EKS (`confidence: "inferred"`). - `google_compute_instance` and `google_container_cluster` are not direct-mapped in `fast-path.md`; use the rubric below (typically `confidence: "inferred"`). - If a resource is not eligible for Direct Mappings (or row conditions are not met), use the rubric below. ## Eliminators (Hard Blockers) -| GCP Service | AWS | Blocker | -| --------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Cloud Run | Lambda | Execution time >15 min → use Fargate | -| Cloud Run | Fargate | GPU workload or >16 vCPU or >120 GB memory → use EC2 | -| Cloud Functions | Lambda | Python version not supported (e.g., Python 2.7) → use custom runtime on Fargate | -| GKE | EKS | Custom CRI incompatible → manual workaround or ECS | -| Any | App Runner | **Closed to new customers (April 30 2026).** Do not target App Runner for new migrations. Use Fargate (default), Lambda (event-driven), or EKS (K8s required). | -| App Engine | Elastic Beanstalk | `compute_model: "container_orchestration"` or `"serverless"` in preferences → do not use EB, fall through to Fargate or Lambda _(preference override, not a technical blocker)_ | +| GCP Service | AWS | Blocker | +| --------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Cloud Run | Lambda | Execution time >15 min → use Fargate | +| Cloud Run | Fargate | GPU workload or >16 vCPU or >120 GB memory → use EC2 | +| Cloud Functions | Lambda | Python version not supported (e.g., Python 2.7) → use custom runtime on Fargate | +| GKE | EKS | Custom CRI incompatible → manual workaround or ECS | +| Any | App Runner | **Closed to new customers (April 30 2026).** Do not target App Runner for new migrations. Use Fargate (default), Lambda (event-driven), or EKS (K8s required). | +| App Engine | Elastic Beanstalk | `compute_model: "container_orchestration"` or `"serverless"` in preferences → do not use EB, fall through to Fargate or Lambda _(preference override, not a technical blocker)_. Also `compute: "eks"` (Q5 = multi-cloud) → do not use EB, route to **EKS** _(top-level portability override)_ | ## Signals (Decision Criteria) @@ -49,11 +49,12 @@ Note: Cloud Run maps to Fargate via deterministic fast-path ("Always"). The `com ### App Engine +- **Multi-cloud portability required** (`compute: "eks"` from Q5) → **EKS** — top-level portability override; takes precedence over the EB default and any `compute_model` preference (Q7b does not fire under multi-cloud). Same override that forces GKE → EKS. - **Default** → Elastic Beanstalk (PaaS-to-PaaS, preserves managed platform model) - **User prefers container control** (`compute_model: "container_orchestration"`) → Fargate - **Event-driven / scale-to-zero required** → Lambda -After selecting Elastic Beanstalk, load `elastic-beanstalk.md` to populate `aws_config` (platform, deployment policy, IAM, VPC, sizing). +After selecting Elastic Beanstalk, load `elastic-beanstalk.md` to populate `aws_config` (platform, deployment policy, IAM, VPC, sizing). When `compute: "eks"` routes App Engine to EKS, do **not** run the EB fan-out or load `elastic-beanstalk.md` — the app_version resources are skipped like any other non-EB path. ## CPU Architecture (Graviton vs x86) @@ -72,11 +73,12 @@ Add a `graviton` block (see `schema-graviton.md`) to the service's output. GPU/C Apply in order; first match wins: -1. **Eliminators**: Does GCP config violate AWS constraints? If yes: switch to alternative +1. **Eliminators**: Does GCP config violate AWS constraints, or does a top-level preference override apply? If yes: switch to alternative. **`compute: "eks"` (Q5 = multi-cloud) is a hard override — App Engine (and all compute) → EKS; stop here, do not evaluate the managed-platform/EB branch below.** 2. **Operational Model**: Managed (Lambda, Fargate) vs Self-Hosted (EC2, EKS)? - Prefer managed unless: Always-on + high baseline cost → EC2 - - For App Engine sources: Elastic Beanstalk (PaaS-to-PaaS) when `compute_model` is absent or `"managed_platform"` -3. **User Preference**: From `preferences.json`: `design_constraints.kubernetes`, `design_constraints.cost_sensitivity`? + - For App Engine sources: Elastic Beanstalk (PaaS-to-PaaS) when `compute_model` is absent or `"managed_platform"` **and `compute` ≠ `"eks"`** (when `compute: "eks"`, the criterion-1 override already selected EKS) +3. **User Preference**: From `preferences.json`: `design_constraints.compute`, `design_constraints.kubernetes`, `design_constraints.cost_sensitivity`? + - If `compute = "eks"` (Q5 = multi-cloud) → **EKS** for all compute, including App Engine (top-level portability override; overrides EB default and `compute_model`) - If `kubernetes = "eks-managed"` → EKS (preserves K8s investment) - If `kubernetes = "eks-or-ecs"` → EKS with managed node groups (user is competent with K8s) - If `kubernetes = "ecs-fargate"` → Fargate (simpler managed containers) @@ -144,6 +146,16 @@ Apply in order; first match wins: - → **AWS: Fargate (0.5 CPU, 1 GB memory)** - Confidence: `inferred` (rubric-based override of default PaaS mapping) +### Example 4c: App Engine (Q5 = multi-cloud portability required) + +- GCP: `google_app_engine_application` with one service (`default`), app_version runtime=python39, instance_class=F2 +- Signals: PaaS deployment, but `compute: "eks"` in preferences (Q5 = multi-cloud); Q7b did not fire, so `compute_model` is absent +- Fast-path condition NOT met: `compute` = `"eks"`, so the Direct Mapping row does not match — falls through to rubric +- Criterion 1 (Eliminators): EB blocked (`compute: "eks"` multi-cloud override) +- Criterion 3 (User Preference): `compute = "eks"` → EKS (same top-level portability override as GKE) +- → **AWS: EKS** (no EB fan-out; app_version resources skipped like any other non-EB path) +- Confidence: `inferred` (rubric-based override of default PaaS mapping) + ## Output Schema Deterministic (fast-path) mappings omit `rubric_applied`; inferred (rubric-based) mappings include it. diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md index db2ceb37..cb3c9076 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md @@ -6,8 +6,8 @@ App Engine → EB fidelity depends on how the workload was discovered: -- **Terraform** (`google_app_engine_application` + `*_app_version` resources) → full mapping: Q7b fires, and the design fan-out emits one EB environment per service with per-service `runtime`/scaling/sizing. This is the primary path. -- **Billing export** (no Terraform) → coarse mapping via the billing design path (`design-billing.md`): App Engine still targets EB, but without per-service or runtime detail (`confidence: billing_inferred`). +- **Terraform** (`google_app_engine_application` + `*_app_version` resources) → full mapping: Q7b fires (only when `compute` ≠ `"eks"`), and the design fan-out emits one EB environment per service with per-service `runtime`/scaling/sizing. This is the primary path. Under `compute: "eks"` (Q5 = multi-cloud) Q7b does not fire and App Engine routes to EKS instead — the fan-out does not run. +- **Billing export** (no Terraform) → coarse mapping via the billing design path (`design-billing.md`): App Engine targets EB (when `compute` ≠ `"eks"`), but without per-service or runtime detail (`confidence: billing_inferred`). Under `compute: "eks"` (Q5 = multi-cloud) the billing path routes App Engine to EKS instead — see `design-billing.md` App Engine multi-cloud override. - **App code only** (no Terraform, no billing) → no App Engine compute inventory is produced today, so no EB mapping fires. - **Live `gcloud` discovery** → App Engine capture is **not yet wired** (tracked as a follow-up to the live-discovery work, PR #149); until then TF-less, billing-less App Engine projects surface App Engine as an unmapped asset rather than an EB target. @@ -21,7 +21,7 @@ This is not a "Terraform required" policy — it reflects which discovery paths ## When to Use -Routing signals — these apply ONLY when `google_app_engine_application` is in the inventory **and `compute_model` is absent or `"managed_platform"`** (Q7b). If the user chose `compute_model: "container_orchestration"` or `"serverless"` (Q7b = B/C), App Engine does **not** map to EB — it routes to Fargate/Lambda via the `compute.md` rubric, and this reference is not the target (see **NOT the Right Choice** below). Routing authority lives in `compute.md` / `fast-path.md` / `design-infra.md`; this file is a supplementary config reference. When the gate holds, any of these signals is sufficient: +Routing signals — these apply ONLY when `google_app_engine_application` is in the inventory **and `compute_model` is absent or `"managed_platform"` and `compute` ≠ `"eks"`** (Q7b). If the user chose `compute_model: "container_orchestration"` or `"serverless"` (Q7b = B/C), App Engine does **not** map to EB — it routes to Fargate/Lambda via the `compute.md` rubric, and this reference is not the target (see **NOT the Right Choice** below). Likewise, if `compute: "eks"` (Q5 = multi-cloud), Q7b does not fire and App Engine routes to **EKS** — this reference is not the target. Routing authority lives in `compute.md` / `fast-path.md` / `design-infra.md`; this file is a supplementary config reference. When the gate holds, any of these signals is sufficient: - `google_app_engine_application` detected in Terraform (strongest PaaS-to-PaaS signal) - User answers `compute_model: "managed_platform"` in Clarify (Q7b) diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md index 5087cea3..c2e1fa89 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/fast-path.md @@ -36,22 +36,22 @@ JSON artifacts **must** keep the `confidence` string values above. When speaking ## Direct Mappings Table -| GCP Service | AWS Service | Conditions | Notes | -| ------------------------------------------- | -------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `google_storage_bucket` | S3 | Always | 1:1 mapping; preserve ACL/versioning/lifecycle rules | -| `google_cloud_run_service` | Fargate | Always | Preferred container runtime target | -| `google_cloud_run_v2_service` | Fargate | Always | v2 API variant of Cloud Run | -| `google_cloudfunctions_function` | Lambda | Always | Gen 1 function mapping | -| `google_cloudfunctions2_function` | Lambda | Always | Gen 2 function mapping | -| `google_sql_database_instance` (SQL Server) | RDS SQL Server | Always | Always provisioned (no serverless) | -| `google_compute_network` | VPC | Always | 1:1; preserve CIDR ranges | -| `google_compute_firewall` | Security Group | Always | 1:1 rule mapping; adjust CIDR if needed | -| `google_dns_managed_zone` | Route 53 Hosted Zone | Always | Preserve zone name and records | -| `google_service_account` | IAM Role | Always | Map permissions directly; adjust service principals | -| `google_secret_manager_secret` | Secrets Manager | Always | Create secret metadata and IAM-scoped access | -| `google_secret_manager_secret_version` | Secrets Manager | Always | Carry current value or explicit migration TODO | -| `google_redis_instance` | ElastiCache Redis | Always | 1:1 mapping; preserve cluster mode and node type | -| `google_app_engine_application` | Elastic Beanstalk | `compute_model` absent or `"managed_platform"` | PaaS-to-PaaS. One EB environment **per App Engine service**; read `runtime`/`instance_class`/scaling from the `*_app_version` resources (see `elastic-beanstalk.md`). If `compute_model: "container_orchestration"` or `"serverless"`, use rubric in `compute.md` (confidence: `inferred`). | +| GCP Service | AWS Service | Conditions | Notes | +| ------------------------------------------- | -------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `google_storage_bucket` | S3 | Always | 1:1 mapping; preserve ACL/versioning/lifecycle rules | +| `google_cloud_run_service` | Fargate | Always | Preferred container runtime target | +| `google_cloud_run_v2_service` | Fargate | Always | v2 API variant of Cloud Run | +| `google_cloudfunctions_function` | Lambda | Always | Gen 1 function mapping | +| `google_cloudfunctions2_function` | Lambda | Always | Gen 2 function mapping | +| `google_sql_database_instance` (SQL Server) | RDS SQL Server | Always | Always provisioned (no serverless) | +| `google_compute_network` | VPC | Always | 1:1; preserve CIDR ranges | +| `google_compute_firewall` | Security Group | Always | 1:1 rule mapping; adjust CIDR if needed | +| `google_dns_managed_zone` | Route 53 Hosted Zone | Always | Preserve zone name and records | +| `google_service_account` | IAM Role | Always | Map permissions directly; adjust service principals | +| `google_secret_manager_secret` | Secrets Manager | Always | Create secret metadata and IAM-scoped access | +| `google_secret_manager_secret_version` | Secrets Manager | Always | Carry current value or explicit migration TODO | +| `google_redis_instance` | ElastiCache Redis | Always | 1:1 mapping; preserve cluster mode and node type | +| `google_app_engine_application` | Elastic Beanstalk | (`compute_model` absent or `"managed_platform"`) **and** `compute` ≠ `"eks"` | PaaS-to-PaaS. One EB environment **per App Engine service**; read `runtime`/`instance_class`/scaling from the `*_app_version` resources (see `elastic-beanstalk.md`). If `compute_model: "container_orchestration"` or `"serverless"`, use rubric in `compute.md` (confidence: `inferred`). If `compute: "eks"` (Q5 = multi-cloud), the row does not match — App Engine follows the container path to **EKS** via the rubric, same portability override as GKE. | ### Cloud SQL PostgreSQL / MySQL — NOT in Direct Mappings @@ -83,23 +83,25 @@ These GCP resources do **not** require AWS equivalents in v1.0: | `google_app_engine_standard_app_version` | **Config source, not a standalone target.** Carries `runtime`/`instance_class`/scaling/`env_variables` that feed the parent's App Engine → Elastic Beanstalk mapping (see note below). Skip as its own resource; log to `warnings`. | | `google_app_engine_flexible_app_version` | **Config source, not a standalone target.** Same as the standard variant (Flexible uses `resources` + `flexible_runtime_settings`). Skip as its own resource; log to `warnings`. | -**Note — App Engine app_version resources are config sources for the EB mapping.** `google_app_engine_standard_app_version` and `google_app_engine_flexible_app_version` are skipped as standalone resources (above), **but their config is read** to build the parent's mapping: `runtime`, `instance_class`, `env_variables`, and scaling live on the _version_ resource, not on the parent `google_app_engine_application`. Discovery classifies them SECONDARY (`configuration`); because they link to the parent only by shared `project` (no ID reference), `serves[]` may be empty and they may cluster separately, so the **App Engine fan-out** step in `phases/design/design-infra.md` locates them by scanning the full inventory and emits **one EB environment per App Engine service** (grouping multiple versions of a service into one), so a multi-service app does not collapse into a single mapping. On the non-EB paths (Q7b container-orchestration/serverless, ambiguous attribution, or no parent) they are simply skipped like any other Skip Mapping — logged to `warnings`, never sent through the rubric or the unknown-type STOP. See `elastic-beanstalk.md` → "Where the config comes from (Terraform)". +**Note — App Engine app_version resources are config sources for the EB mapping.** `google_app_engine_standard_app_version` and `google_app_engine_flexible_app_version` are skipped as standalone resources (above), **but their config is read** to build the parent's mapping: `runtime`, `instance_class`, `env_variables`, and scaling live on the _version_ resource, not on the parent `google_app_engine_application`. Discovery classifies them SECONDARY (`configuration`); because they link to the parent only by shared `project` (no ID reference), `serves[]` may be empty and they may cluster separately, so the **App Engine fan-out** step in `phases/design/design-infra.md` locates them by scanning the full inventory and emits **one EB environment per App Engine service** (grouping multiple versions of a service into one), so a multi-service app does not collapse into a single mapping. On the non-EB paths (Q7b container-orchestration/serverless, `compute: "eks"` multi-cloud, ambiguous attribution, or no parent) they are simply skipped like any other Skip Mapping — logged to `warnings`, never sent through the rubric or the unknown-type STOP. See `elastic-beanstalk.md` → "Where the config comes from (Terraform)". ## Preferred AWS Target Services For the workload categories below, always steer toward the **preferred** AWS service. If rubric evaluation or LLM reasoning would select a non-preferred service, substitute the preferred alternative instead. -| Workload Category | Preferred AWS Target | Rationale | -| ----------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | -| Containerized workloads | Fargate (default), Lambda (event-driven), EKS (K8s required) | Deeper integration with VPC, ALB, IAM, and auto-scaling than lighter-weight alternatives | -| PaaS / managed platform | Elastic Beanstalk — **only when `compute_model` is absent or `"managed_platform"`** | Preserves managed platform model; AWS manages deployments, scaling, patching, monitoring | -| Third-party auth in use | Keep existing auth provider | Startups using Auth0, Firebase Auth, Clerk, etc. should preserve their existing investment | +| Workload Category | Preferred AWS Target | Rationale | +| ----------------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| Containerized workloads | Fargate (default), Lambda (event-driven), EKS (K8s required) | Deeper integration with VPC, ALB, IAM, and auto-scaling than lighter-weight alternatives | +| PaaS / managed platform | Elastic Beanstalk — **only when `compute_model` is absent or `"managed_platform"` and `compute` ≠ `"eks"`** | Preserves managed platform model; AWS manages deployments, scaling, patching, monitoring | +| Third-party auth in use | Keep existing auth provider | Startups using Auth0, Firebase Auth, Clerk, etc. should preserve their existing investment | **Enforcement:** Check every resource mapping against this table before writing to `aws-design.json` or `aws-design-billing.json`. If a mapping would select a non-preferred service, substitute the preferred alternative. **Enforcement exemption (App Engine + explicit compute_model):** When the user set `compute_model: "container_orchestration"` or `"serverless"` (Q7b = B/C), the App Engine eliminator in `compute.md` intentionally routes the resource to Fargate/Lambda. **Do not** apply the PaaS row to re-substitute Elastic Beanstalk in that case — the PaaS row's condition (`compute_model` absent or `"managed_platform"`) is already false, so it does not match. This mirrors how Cloud Run is exempt (always Containerized) and Cloud SQL is exempt (Q6 governs RDS-vs-Aurora). -**Precedence:** Match resources to the **first applicable row**. Cloud Run matches "Containerized workloads" regardless of `compute_model`. The "PaaS / managed platform" row applies to App Engine resources **only when its `compute_model` condition holds** (absent or `"managed_platform"`); an App Engine resource with `compute_model: "container_orchestration"`/`"serverless"` matches no row here and keeps its rubric-selected Fargate/Lambda target. +**Enforcement exemption (App Engine + `compute: "eks"` / Q5 = multi-cloud):** When `compute: "eks"` is set (Q5 = multi-cloud), Q7b does not fire and App Engine routes to **EKS** via the container path (see `compute.md` App Engine section). **Do not** apply the PaaS row to re-substitute Elastic Beanstalk in that case — the PaaS row's condition (`compute` ≠ `"eks"`) is already false, so it does not match. This is the same top-level portability override that forces GKE to EKS. + +**Precedence:** Match resources to the **first applicable row**. Cloud Run matches "Containerized workloads" regardless of `compute_model`. The "PaaS / managed platform" row applies to App Engine resources **only when its `compute_model` condition holds** (absent or `"managed_platform"`) **and `compute` ≠ `"eks"`**; an App Engine resource with `compute_model: "container_orchestration"`/`"serverless"` — or with `compute: "eks"` (multi-cloud) — matches no row here and keeps its rubric-selected target (Fargate/Lambda, or EKS under multi-cloud). **Exception:** For Cloud SQL PostgreSQL/MySQL, **Q6 availability always overrides** any implicit Aurora preference. Do not substitute Aurora when `availability` is `single-az` or `multi-az`. diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/index.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/index.md index c5c51f92..cc084afe 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/index.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/index.md @@ -4,13 +4,13 @@ ## Compute Services -| GCP Service | Resource Type | Reference File | Typical AWS target | -| ------------------- | -------------------------------- | -------------- | ----------------------------------------------- | -| Cloud Run | `google_cloud_run_service` | `compute.md` | Fargate | -| Cloud Functions | `google_cloudfunctions_function` | `compute.md` | Lambda | -| Compute Engine (VM) | `google_compute_instance` | `compute.md` | EC2 or Fargate | -| GKE | `google_container_cluster` | `compute.md` | EKS | -| App Engine | `google_app_engine_application` | `compute.md` | Elastic Beanstalk (default), Fargate, or Lambda | +| GCP Service | Resource Type | Reference File | Typical AWS target | +| ------------------- | -------------------------------- | -------------- | ----------------------------------------------------------------------------------------------- | +| Cloud Run | `google_cloud_run_service` | `compute.md` | Fargate | +| Cloud Functions | `google_cloudfunctions_function` | `compute.md` | Lambda | +| Compute Engine (VM) | `google_compute_instance` | `compute.md` | EC2 or Fargate | +| GKE | `google_container_cluster` | `compute.md` | EKS | +| App Engine | `google_app_engine_application` | `compute.md` | Elastic Beanstalk (default), Fargate, Lambda, or EKS (when `compute: "eks"` / Q5 = multi-cloud) | ## Database Services diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify-compute.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify-compute.md index 542ccd1b..c8bc24b9 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify-compute.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify-compute.md @@ -37,7 +37,7 @@ _Fire when:_ Compute resources present (Cloud Run, Cloud Functions, GKE, GCE, Ap ## Q7b — What compute operational model do you prefer for your App Engine workloads? -_Fire when:_ App Engine present in inventory (`google_app_engine_application`). Skip when: no App Engine in inventory. +_Fire when:_ App Engine present in inventory (`google_app_engine_application`) AND Q5 != A (multi-cloud). Skip when: no App Engine in inventory, or Q5 = A (multi-cloud already resolved compute to EKS — App Engine routes to EKS, overriding the EB default; same portability override as Q8). **Rationale:** GCP App Engine is a PaaS that can map to different AWS compute targets depending on whether the user wants to preserve the managed platform model (Elastic Beanstalk), switch to direct container control (Fargate/ECS), or go serverless (Lambda). This drives the fundamental routing decision for App Engine resources. @@ -68,6 +68,8 @@ D -> same as default (A) **Default:** **A** (`compute_model: "managed_platform"`). App Engine is PaaS; Elastic Beanstalk is the closest AWS equivalent. Users who skip or say "I don't know" get the PaaS-to-PaaS path. +_Note: If Q5=Yes (multi-cloud), this question is skipped — `compute: "eks"` is already decided and App Engine routes to EKS, overriding the EB default (mirrors Q8)._ + --- ## Q8 — How does your team feel about managing Kubernetes? diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify.md index 7cf88fe8..1b558457 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/clarify/clarify.md @@ -278,7 +278,7 @@ Every question in an **active** category gets exactly one disposition: | Q5 | PROPOSED | B — AWS-only | Assuming AWS-only → ECS Fargate eligible; if multi-cloud portability is required, all containers go to EKS instead | | Q6 | DETECTED when all Cloud SQL instances agree; **ESSENTIAL** on conflict/missing; PROPOSED when no Cloud SQL signal but DB present | B — `multi-az` | Assuming Multi-AZ RDS → automatic failover, roughly 2x single-AZ database cost; say "single-az" for dev-grade, "mission-critical" for Aurora (**unconfirmed default — roughly halves or doubles the database estimate; flagged in the report**) | | Q7 | **ESSENTIAL — always** | — | — (cutover strategy selects DMS vs pg_dump/pgcopydb and the entire migration runbook shape; never assumed) | -| Q7b | PROPOSED when App Engine present; N/A otherwise | A — `managed_platform` | Assuming managed platform → App Engine maps to Elastic Beanstalk; say "container orchestration" for ECS Fargate or "serverless" for Lambda | +| Q7b | PROPOSED when App Engine present and Q5 ≠ multi-cloud; N/A otherwise | A — `managed_platform` | Assuming managed platform → App Engine maps to Elastic Beanstalk; say "container orchestration" for ECS Fargate or "serverless" for Lambda | | Cat B | PROPOSED (each prompt, billing-only mode) | Zonal / 1 service / estimate / Gen 1 | Fills config gaps billing can't answer; corrections here change sizing inputs | | Q8 | PROPOSED (only when GKE present and Q5 ≠ multi-cloud) | C — `ecs-fargate` | Assuming Fargate → no Kubernetes to operate; answer "EKS" to preserve your K8s investment | | Q9 | DETECTED when code scan found none; ESSENTIAL when scan found matches (confirm); PROPOSED when no code was analyzed | B — no WebSockets | Assuming no WebSockets → standard ALB config; correct this if you have realtime/persistent-connection features (**unverified — no code scan**) | @@ -309,8 +309,8 @@ Every question in an **active** category gets exactly one disposition: Apply before finalizing dispositions: -- **Q5 answered/overridden to "multi-cloud"** — Immediately record `compute: "eks"`. Q8 becomes N/A (early-exit). -- **Q7b N/A** — App Engine (`google_app_engine_application`) not present in inventory. +- **Q5 answered/overridden to "multi-cloud"** — Immediately record `compute: "eks"`. Q8 **and Q7b** become N/A (early-exit); App Engine follows the container path to **EKS**, overriding the default EB mapping. +- **Q7b N/A** — App Engine (`google_app_engine_application`) not present in inventory, or Q5 resolved to multi-cloud (App Engine → EKS, same override as Q8). - **Q10/Q11 N/A** — Cloud Run not present. - **Q12/Q13/Q13b N/A** — Cloud SQL (PostgreSQL or MySQL) not present in inventory. - **Q8 N/A** — No GKE in inventory, or Q5 resolved to multi-cloud. @@ -379,20 +379,20 @@ A) Most conservative (highest HA) | B) Use [instance name] as primary | C) Ask m **Override handling** — when the user corrects a value (detected or assumed): -| User correction (examples) | Update constraint | Re-ask? | -| ------------------------------------------------ | -------------------------------------------------- | ------------------------- | -| `availability: mission-critical` / `multi-az-ha` | `availability: "multi-az-ha"`, `chosen_by: "user"` | No — value is explicit | -| `availability: significant` / `multi-az` | `availability: "multi-az"`, `chosen_by: "user"` | No | -| `availability: dev` / `single-az` | `availability: "single-az"`, `chosen_by: "user"` | No | -| `db size: <10GB` / `10-100GB` / etc. | Set `db_size` to stated band, `chosen_by: "user"` | No if band is explicit | -| `region: [AWS region]` | Set `target_region`, `chosen_by: "user"` | No | -| `model: [model name]` | Set `ai_model_baseline`, `chosen_by: "user"` | No if maps cleanly to Q19 | -| `websockets: yes` | Set `websocket: "required"`, `chosen_by: "user"` | No | -| `spend: $5K-$20K` | Set `gcp_monthly_spend`, `chosen_by: "user"` | No if band is explicit | -| `ai priority: cost` / `speed` / `quality` | Set `ai_priority`, `chosen_by: "user"` | No | -| `multi-cloud: yes` | `compute: "eks"`, `chosen_by: "user"`; Q8 → N/A | No | -| "ask me about [setting]" | Convert that row to ESSENTIAL | Yes — full question | -| Vague correction ("that's wrong") | Convert that row to ESSENTIAL | Yes — full question | +| User correction (examples) | Update constraint | Re-ask? | +| ------------------------------------------------ | ----------------------------------------------------------------------------- | ------------------------- | +| `availability: mission-critical` / `multi-az-ha` | `availability: "multi-az-ha"`, `chosen_by: "user"` | No — value is explicit | +| `availability: significant` / `multi-az` | `availability: "multi-az"`, `chosen_by: "user"` | No | +| `availability: dev` / `single-az` | `availability: "single-az"`, `chosen_by: "user"` | No | +| `db size: <10GB` / `10-100GB` / etc. | Set `db_size` to stated band, `chosen_by: "user"` | No if band is explicit | +| `region: [AWS region]` | Set `target_region`, `chosen_by: "user"` | No | +| `model: [model name]` | Set `ai_model_baseline`, `chosen_by: "user"` | No if maps cleanly to Q19 | +| `websockets: yes` | Set `websocket: "required"`, `chosen_by: "user"` | No | +| `spend: $5K-$20K` | Set `gcp_monthly_spend`, `chosen_by: "user"` | No if band is explicit | +| `ai priority: cost` / `speed` / `quality` | Set `ai_priority`, `chosen_by: "user"` | No | +| `multi-cloud: yes` | `compute: "eks"`, `chosen_by: "user"`; Q8 → N/A; Q7b → N/A (App Engine → EKS) | No | +| "ask me about [setting]" | Convert that row to ESSENTIAL | Yes — full question | +| Vague correction ("that's wrong") | Convert that row to ESSENTIAL | Yes — full question | For each override: set `chosen_by: "user"` on the constraint (this removes the `source` field since it's no longer extracted/default). For extracted rows, also remove the question ID from `metadata.questions_skipped_extracted`; for assumed rows, remove it from `metadata.questions_defaulted`. @@ -494,48 +494,48 @@ If user opts in, present Q-E1–Q-E2 (defined in **Category E — Migration Post ## Answer Combination Triggers -| Scenario | Key Answers | Recommendation | -| ---------------------------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | -| Early-stage funding path | Q3 = lower spend band | Entry-tier migration funding program review | -| Growth-stage funding path | Q3 = higher spend band | Migration funding/support program review based on spend profile | -| Managed platform preference | Q7b = A (managed platform) | Elastic Beanstalk for App Engine compute targets | -| Container orchestration pref | Q7b = B (container orchestration) | ECS Fargate for App Engine targets (overrides default EB mapping) | -| Serverless pref | Q7b = C (serverless) | Lambda for App Engine targets (overrides default EB mapping) | -| Must stay portable | Q5 = Yes multi-cloud | EKS only, no ECS Fargate | -| Kubernetes-averse | Q5 = No + Q8 = Frustrated | ECS Fargate strongly recommended | -| WebSocket app | Q9 = Yes | ALB WebSocket config required | -| Low-traffic Cloud Run | Q10 = Business hours + Q11 < $100 | Recommend staying on Cloud Run | -| Cloud SQL Postgres — dev/low HA | Q6 = Inconvenient + Cloud SQL in inventory | **RDS PostgreSQL** single-AZ | -| Cloud SQL Postgres — prod HA (RDS) | Q6 = Significant Issue + Cloud SQL in inventory | **RDS PostgreSQL** Multi-AZ | -| Cloud SQL Postgres — mission-critical | Q6 = Mission-Critical + Cloud SQL in inventory | **Aurora PostgreSQL** Multi-AZ; apply Q12/Q13 | -| Cloud SQL Postgres — global catastrophic | Q6 = Catastrophic + Q1 = Global + Cloud SQL in inventory | **Aurora PostgreSQL Global Database** | -| High I/O database (RDS path) | Q6 = Inconvenient/Significant + Q13 = High | **RDS** io2 or Provisioned IOPS | -| High I/O database (Aurora path) | Q6 = Mission-Critical/Catastrophic + Q13 = High | Aurora I/O-Optimized | -| Write-heavy global DB | Q6 = Mission-Critical/Catastrophic + Q12 = Write-heavy/global | Aurora DSQL architecture review (RDS path only: size writer; flag review) | -| Rapidly growing DB (RDS path) | Q6 = Inconvenient/Significant + Q12 = Rapidly growing | RDS with headroom on instance class | -| Rapidly growing DB (Aurora path) | Q6 = Mission-Critical/Catastrophic + Q12 = Rapidly growing | Aurora Serverless v2 | -| Zero downtime required | Q7 = No downtime | Blue/green + AWS DMS required (RDS or Aurora blue/green per Q6) | -| HIPAA compliance | Q2 = HIPAA | BAA services only, specific regions | -| FedRAMP required | Q2 = FedRAMP | GovCloud regions only | -| CCPA / CPRA | Q2 = G (CCPA / CPRA) | Consumer privacy, logging/retention, data-inventory posture; confirm regions with legal review | -| Gateway-only AI | Q14 = B only (LLM router/gateway) | Config change only; skip SDK migration | -| LangChain/LangGraph AI | Q14 includes C | Provider swap via ChatBedrock; 1–3 days | -| OpenAI Agents SDK | Q14 includes E | Highest AI effort; AgentCore (Harness/Runtime); 2–4 weeks | -| Multi-agent + MCP | Q14 = D + F | AgentCore to unify orchestration + MCP (Gateway) | -| Voice platform AI | Q14 includes G | Check native Bedrock support; Nova 2 Sonic if needed | -| GPT-5.5 migration | Q19 = GPT-5.5 | Claude Opus 4.6 — Bedrock 17% cheaper on output; or Sonnet 4.6 for 53% savings | -| GPT-5.5 Pro migration | Q19 = GPT-5.5 Pro | Nova 2 Pro — 95% cheaper on Bedrock | -| GPT-5.4 migration | Q19 = GPT-5.4 | Claude Sonnet 4.6 — near price parity; AWS consolidation | -| GPT-5.4 Mini/Nano migration | Q19 = GPT-5.4 Mini or Nano | Nova Lite/Micro — 87-94% cheaper on Bedrock | -| GPT-4 Turbo migration | Q19 = GPT-4 Turbo | Claude Sonnet 4.6 — 70% cheaper on input | -| o-series migration | Q19 = o-series | Claude Sonnet 4.6 with extended thinking | -| High-volume cost-critical AI | Q18 = High + cost critical | Nova Micro or Haiku 4.5 + provisioned throughput | -| Reasoning/agent workload | Q17 = Extended thinking | Claude Sonnet 4.6 extended thinking; Opus 4.6 for hardest | -| Speech-to-speech AI | Q17 = Real-time speech | Nova 2 Sonic | -| RAG workload | Q17 = RAG optimization | Bedrock Knowledge Bases + Titan Embeddings | -| Vision workload | Q20 = Vision required | Claude Sonnet 4.6 (multimodal) | -| Latency-critical AI | Q21 = Critical | Haiku 4.5 or Nova Micro + streaming | -| Complex reasoning tasks | Q22 = Complex | Claude Sonnet 4.6; Opus 4.6 for hardest | +| Scenario | Key Answers | Recommendation | +| ---------------------------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| Early-stage funding path | Q3 = lower spend band | Entry-tier migration funding program review | +| Growth-stage funding path | Q3 = higher spend band | Migration funding/support program review based on spend profile | +| Managed platform preference | Q7b = A (managed platform) | Elastic Beanstalk for App Engine compute targets | +| Container orchestration pref | Q7b = B (container orchestration) | ECS Fargate for App Engine targets (overrides default EB mapping) | +| Serverless pref | Q7b = C (serverless) | Lambda for App Engine targets (overrides default EB mapping) | +| Must stay portable | Q5 = Yes multi-cloud | EKS only, no ECS Fargate; App Engine also routes to EKS (Q7b N/A, overrides default EB mapping) | +| Kubernetes-averse | Q5 = No + Q8 = Frustrated | ECS Fargate strongly recommended | +| WebSocket app | Q9 = Yes | ALB WebSocket config required | +| Low-traffic Cloud Run | Q10 = Business hours + Q11 < $100 | Recommend staying on Cloud Run | +| Cloud SQL Postgres — dev/low HA | Q6 = Inconvenient + Cloud SQL in inventory | **RDS PostgreSQL** single-AZ | +| Cloud SQL Postgres — prod HA (RDS) | Q6 = Significant Issue + Cloud SQL in inventory | **RDS PostgreSQL** Multi-AZ | +| Cloud SQL Postgres — mission-critical | Q6 = Mission-Critical + Cloud SQL in inventory | **Aurora PostgreSQL** Multi-AZ; apply Q12/Q13 | +| Cloud SQL Postgres — global catastrophic | Q6 = Catastrophic + Q1 = Global + Cloud SQL in inventory | **Aurora PostgreSQL Global Database** | +| High I/O database (RDS path) | Q6 = Inconvenient/Significant + Q13 = High | **RDS** io2 or Provisioned IOPS | +| High I/O database (Aurora path) | Q6 = Mission-Critical/Catastrophic + Q13 = High | Aurora I/O-Optimized | +| Write-heavy global DB | Q6 = Mission-Critical/Catastrophic + Q12 = Write-heavy/global | Aurora DSQL architecture review (RDS path only: size writer; flag review) | +| Rapidly growing DB (RDS path) | Q6 = Inconvenient/Significant + Q12 = Rapidly growing | RDS with headroom on instance class | +| Rapidly growing DB (Aurora path) | Q6 = Mission-Critical/Catastrophic + Q12 = Rapidly growing | Aurora Serverless v2 | +| Zero downtime required | Q7 = No downtime | Blue/green + AWS DMS required (RDS or Aurora blue/green per Q6) | +| HIPAA compliance | Q2 = HIPAA | BAA services only, specific regions | +| FedRAMP required | Q2 = FedRAMP | GovCloud regions only | +| CCPA / CPRA | Q2 = G (CCPA / CPRA) | Consumer privacy, logging/retention, data-inventory posture; confirm regions with legal review | +| Gateway-only AI | Q14 = B only (LLM router/gateway) | Config change only; skip SDK migration | +| LangChain/LangGraph AI | Q14 includes C | Provider swap via ChatBedrock; 1–3 days | +| OpenAI Agents SDK | Q14 includes E | Highest AI effort; AgentCore (Harness/Runtime); 2–4 weeks | +| Multi-agent + MCP | Q14 = D + F | AgentCore to unify orchestration + MCP (Gateway) | +| Voice platform AI | Q14 includes G | Check native Bedrock support; Nova 2 Sonic if needed | +| GPT-5.5 migration | Q19 = GPT-5.5 | Claude Opus 4.6 — Bedrock 17% cheaper on output; or Sonnet 4.6 for 53% savings | +| GPT-5.5 Pro migration | Q19 = GPT-5.5 Pro | Nova 2 Pro — 95% cheaper on Bedrock | +| GPT-5.4 migration | Q19 = GPT-5.4 | Claude Sonnet 4.6 — near price parity; AWS consolidation | +| GPT-5.4 Mini/Nano migration | Q19 = GPT-5.4 Mini or Nano | Nova Lite/Micro — 87-94% cheaper on Bedrock | +| GPT-4 Turbo migration | Q19 = GPT-4 Turbo | Claude Sonnet 4.6 — 70% cheaper on input | +| o-series migration | Q19 = o-series | Claude Sonnet 4.6 with extended thinking | +| High-volume cost-critical AI | Q18 = High + cost critical | Nova Micro or Haiku 4.5 + provisioned throughput | +| Reasoning/agent workload | Q17 = Extended thinking | Claude Sonnet 4.6 extended thinking; Opus 4.6 for hardest | +| Speech-to-speech AI | Q17 = Real-time speech | Nova 2 Sonic | +| RAG workload | Q17 = RAG optimization | Bedrock Knowledge Bases + Titan Embeddings | +| Vision workload | Q20 = Vision required | Claude Sonnet 4.6 (multimodal) | +| Latency-critical AI | Q21 = Critical | Haiku 4.5 or Nova Micro + streaming | +| Complex reasoning tasks | Q22 = Complex | Claude Sonnet 4.6; Opus 4.6 for hardest | --- @@ -778,7 +778,7 @@ Documented defaults for every question. Used by: PROPOSED sheet rows (wizard), p | Q5 — Multi-cloud | B (AWS-only) | no constraint | | Q6 — Uptime | B (significant) | `availability: "multi-az"` | | Q7 — Maintenance | D (flexible) | `cutover_strategy: "flexible"` | -| Q7b — Compute model | A (managed platform) | `compute_model: "managed_platform"` (App Engine only; N/A if no App Engine) | +| Q7b — Compute model | A (managed platform) | `compute_model: "managed_platform"` (App Engine only; N/A if no App Engine, or if Q5 = multi-cloud → App Engine routes to EKS) | | Cat B — Cloud SQL HA | Zonal | `metadata.inventory_clarifications` | | Cat B — Cloud Run count | 1 service | `metadata.inventory_clarifications` | | Cat B — Memorystore memory | estimate from usage | `metadata.inventory_clarifications` | diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-billing.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-billing.md index b2a13c0a..98b6e86e 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-billing.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-billing.md @@ -56,7 +56,7 @@ Look up `gcp_service_type` in the table below. These are default mappings for co | `google_cloudfunctions_function` | Cloud Functions | Lambda | Fargate | | `google_compute_instance` | Compute Engine | EC2 | Fargate, ASG | | `google_container_cluster` | GKE | EKS | ECS, Fargate | -| `google_app_engine_application` | App Engine | Elastic Beanstalk | Fargate (`compute_model: container_orchestration`), Lambda (`serverless`) | +| `google_app_engine_application` | App Engine | Elastic Beanstalk | **EKS (`compute: "eks"` / Q5 = multi-cloud — overrides the EB default)**, Fargate (`compute_model: container_orchestration`), Lambda (`serverless`) | | `google_firestore_database` | Firestore | DynamoDB | — | | `google_bigquery_dataset` | BigQuery | **`Deferred — specialist engagement`** | **No** Athena/Redshift/Glue in automated output. **`human_expertise_required: true`**. User must engage **AWS account team** and/or **data analytics migration partner**. | | `google_compute_forwarding_rule` | Cloud Load Balancing | ALB | NLB | @@ -67,6 +67,8 @@ Look up `gcp_service_type` in the table below. These are default mappings for co If found: assign the Default AWS Target. Set rationale to: "Billing heuristic: [GCP service] → [AWS service]. Provide Terraform files for configuration-aware mapping." **Exception:** For BigQuery, use: "Billing indicates BigQuery spend — **no automated AWS analytics target**; engage AWS account team / data analytics migration partner (`Deferred — specialist engagement`)." +**App Engine multi-cloud override (applies even in billing mode):** If `gcp_service_type` is `google_app_engine_application` **and** `design_constraints.compute.value == "eks"` (Q5 = multi-cloud), assign **EKS** instead of the Elastic Beanstalk default — the portability override is a top-level design constraint, not a configuration detail, so it applies on the billing path too (same override that forces GKE → EKS). Set rationale to: "Billing heuristic: App Engine → EKS (multi-cloud portability required — overrides Elastic Beanstalk default). Provide Terraform files for configuration-aware mapping." `confidence: billing_inferred`. + **Set `human_expertise_required`**: If `gcp_service_type` is `google_bigquery_dataset` (or billing rows clearly represent BigQuery analytics), set `human_expertise_required: true` and `aws_service` to **`Deferred — specialist engagement`** (same rules as `design-infra.md` BigQuery gate). For all other services, set `human_expertise_required: false`. This field is REQUIRED on every service in the output. **Preferred AWS target check**: **Skip** when `aws_service` is **`Deferred — specialist engagement`**. Otherwise verify the assigned `aws_service` aligns with the Preferred AWS Target Services table in `design-refs/fast-path.md`. If a non-preferred service is selected (e.g., App Runner for containerized workloads), substitute the preferred alternative (e.g., Fargate). Add a note to the rationale: "Preferred target: [alternative] selected for stronger ecosystem integration." diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md index fc1026bf..7f8d3573 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-infra.md @@ -26,9 +26,9 @@ For each PRIMARY resource in the cluster: 2. Look up in `design-refs/fast-path.md` → **Direct Mappings** table (not the Preferred Target table — that applies later in Pass 2). 3. If found, evaluate the row's **Conditions** column: - `Always` → conditions met. - - Conditional rows (e.g., `google_app_engine_application` requires `compute_model` absent or `"managed_platform"`): read `preferences.json → design_constraints.compute_model.value`. If the condition is NOT met (e.g., user chose `"container_orchestration"` or `"serverless"`), treat as **not found** — proceed to Pass 2. + - Conditional rows (e.g., `google_app_engine_application` requires (`compute_model` absent or `"managed_platform"`) **and** `compute` ≠ `"eks"`): read `preferences.json → design_constraints.compute_model.value` and `design_constraints.compute.value`. If the condition is NOT met (e.g., user chose `"container_orchestration"` or `"serverless"`, **or** `compute: "eks"` from Q5 = multi-cloud), treat as **not found** — proceed to Pass 2. Under `compute: "eks"`, the rubric routes App Engine to **EKS** (portability override, same as GKE); the EB fan-out below does **not** run. 4. If found and conditions match: assign AWS service with confidence = **`deterministic`**. Set `human_expertise_required: false` (no Direct Mapping row requires it). - - **App Engine fan-out (`google_app_engine_application` only):** the parent resource carries no workload config — `runtime`, `instance_class`, `env_variables`, and scaling live on the `google_app_engine_standard_app_version` / `google_app_engine_flexible_app_version` resources. Execute these sub-steps in order: + - **App Engine fan-out (`google_app_engine_application` only; EB path only — skipped when `compute: "eks"` routed App Engine to EKS or when `compute_model` is `"container_orchestration"`/`"serverless"`):** the parent resource carries no workload config — `runtime`, `instance_class`, `env_variables`, and scaling live on the `google_app_engine_standard_app_version` / `google_app_engine_flexible_app_version` resources. Execute these sub-steps in order: 1. **Locate the version resources.** They do **not** reference the parent by ID (App Engine allows one app per project, so the link is the shared `project`), so they are **not** reliably in the parent's cluster or `serves[]`. Scan the **entire `gcp-resource-inventory.json`** across all clusters for app_version resources — do not rely on cluster membership. 2. **Attribute them to this parent.** If there is exactly **one** `google_app_engine_application` in the inventory, all app_version resources belong to it. If there are **multiple** parents, match each app_version to the parent with the same `project`; if `project` is absent on the resources (provider-level in Terraform) with multiple parents, you cannot attribute reliably — map each parent to a single EB environment, add a `warnings` entry naming the ambiguity, and skip sub-steps 4–6 for the ambiguous parents. @@ -92,13 +92,13 @@ If rubric or fast-path would select Aurora when `availability` is `single-az` or 1. **Set `human_expertise_required`**: If the BigQuery specialist gate applied, already `true`. Otherwise set `false` unless another rubric explicitly requires it. This field is REQUIRED on every resource in the output. -1. **Preferred AWS target check**: **Skip** if `aws_service` is **`Deferred — specialist engagement`**. **Skip Aurora substitution** for Cloud SQL when Q6 availability is `single-az` or `multi-az` (RDS is correct). **Skip EB substitution** for App Engine when `compute_model` is `"container_orchestration"` or `"serverless"` (the eliminator's Fargate/Lambda target is correct — the PaaS row's condition is false, see `fast-path.md` enforcement exemption). Otherwise verify the selected `aws_service` aligns with the Preferred AWS Target Services table in `design-refs/fast-path.md`. If a non-preferred service is selected (e.g., App Runner for containerized workloads), substitute the preferred alternative (e.g., Fargate). Add a note to the rationale: "Preferred target: [alternative] selected for stronger ecosystem integration." +1. **Preferred AWS target check**: **Skip** if `aws_service` is **`Deferred — specialist engagement`**. **Skip Aurora substitution** for Cloud SQL when Q6 availability is `single-az` or `multi-az` (RDS is correct). **Skip EB substitution** for App Engine when `compute_model` is `"container_orchestration"` or `"serverless"` (the eliminator's Fargate/Lambda target is correct — the PaaS row's condition is false, see `fast-path.md` enforcement exemption), **or when `compute` is `"eks"`** (Q5 = multi-cloud — the rubric's EKS target is correct; the PaaS row's `compute` ≠ `"eks"` condition is false, see `fast-path.md` multi-cloud enforcement exemption). Otherwise verify the selected `aws_service` aligns with the Preferred AWS Target Services table in `design-refs/fast-path.md`. If a non-preferred service is selected (e.g., App Runner for containerized workloads), substitute the preferred alternative (e.g., Fargate). Add a note to the rationale: "Preferred target: [alternative] selected for stronger ecosystem integration." ## Step 3: Handle Secondary Resources For each SECONDARY resource: -0. **App Engine version resources** (`google_app_engine_standard_app_version` / `google_app_engine_flexible_app_version`): these are **Skip Mappings** — config sources for the App Engine → EB mapping with no standalone AWS target (see `fast-path.md`). If the App Engine fan-out (Pass 1 step 4) already consumed this version, it has already logged the `warnings[]` entry — do nothing here. Otherwise (the fan-out did not run — `container_orchestration`/`serverless` path, ambiguous attribution, or no parent), skip it now: **do not** emit it to `aws-design.json`, **do not** send it through `index.md`/rubric, and add one `warnings[]` entry for it. Either way it never reaches the Pass 2 unknown-type catch-all — so no path STOPs on it, and each version is warned exactly once. +0. **App Engine version resources** (`google_app_engine_standard_app_version` / `google_app_engine_flexible_app_version`): these are **Skip Mappings** — config sources for the App Engine → EB mapping with no standalone AWS target (see `fast-path.md`). If the App Engine fan-out (Pass 1 step 4) already consumed this version, it has already logged the `warnings[]` entry — do nothing here. Otherwise (the fan-out did not run — `container_orchestration`/`serverless` path, `compute: "eks"` multi-cloud path, ambiguous attribution, or no parent), skip it now: **do not** emit it to `aws-design.json`, **do not** send it through `index.md`/rubric, and add one `warnings[]` entry for it. Either way it never reaches the Pass 2 unknown-type catch-all — so no path STOPs on it, and each version is warned exactly once. 1. Use `design-refs/index.md` for category 2. Apply fast-path (most secondaries have deterministic mappings) 3. If rubric needed: apply the **BigQuery specialist gate** (Pass 2 step 0) first when `gcp_type` starts with `google_bigquery_`; otherwise apply the same 6-criteria approach as Pass 2 From cfb014f384b17159528d009de453ed1771171b0c Mon Sep 17 00:00:00 2001 From: Paul Pollack Date: Fri, 24 Jul 2026 18:00:44 -0400 Subject: [PATCH 8/8] fix(gcp-to-aws): clarify billing-table App Engine EKS override column MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The App Engine billing-heuristic row put the `compute: "eks"` multi-cloud override note under the column headed "Alternatives (chosen by IaC path)", which read as if EKS only applied on the IaC path — an LLM reading the row alone would route a multi-cloud App Engine billing migration to Elastic Beanstalk. Point the row explicitly at the multi-cloud override prose directly below the table (which already applies the override on the billing path), removing the contradiction without changing behavior. --- .../gcp-to-aws/references/phases/design/design-billing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-billing.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-billing.md index 98b6e86e..43a2c680 100644 --- a/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-billing.md +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/phases/design/design-billing.md @@ -56,7 +56,7 @@ Look up `gcp_service_type` in the table below. These are default mappings for co | `google_cloudfunctions_function` | Cloud Functions | Lambda | Fargate | | `google_compute_instance` | Compute Engine | EC2 | Fargate, ASG | | `google_container_cluster` | GKE | EKS | ECS, Fargate | -| `google_app_engine_application` | App Engine | Elastic Beanstalk | **EKS (`compute: "eks"` / Q5 = multi-cloud — overrides the EB default)**, Fargate (`compute_model: container_orchestration`), Lambda (`serverless`) | +| `google_app_engine_application` | App Engine | Elastic Beanstalk | Fargate (`compute_model: container_orchestration`), Lambda (`serverless`); EKS under `compute: "eks"` — see multi-cloud override below | | `google_firestore_database` | Firestore | DynamoDB | — | | `google_bigquery_dataset` | BigQuery | **`Deferred — specialist engagement`** | **No** Athena/Redshift/Glue in automated output. **`human_expertise_required: true`**. User must engage **AWS account team** and/or **data analytics migration partner**. | | `google_compute_forwarding_rule` | Cloud Load Balancing | ALB | NLB |