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 204ab66b..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 @@ -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 @@ -33,16 +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/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. @@ -280,6 +281,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/clustering/terraform/classification-rules.md b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/clustering/terraform/classification-rules.md index f03ddace..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,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 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 521b3563..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,27 +5,31 @@ **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`) **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). | +| 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) -### 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 @@ -43,6 +47,15 @@ - **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) +### 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). 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) After selecting the AWS compute service, set its CPU architecture. **Load** `references/shared/graviton.md` (tier behavior) and `references/shared/schema-graviton.md` (the `graviton` block schema). @@ -60,10 +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 -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) @@ -81,29 +96,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) @@ -115,8 +127,41 @@ Apply in order; first match wins: - → **AWS: EC2 t4g.medium + Auto Scaling Group (min=0 in dev)** (Graviton default; use t3.medium if `cpu_architecture` is `x86` or the workload is incompatible — see CPU Architecture section) - Confidence: `inferred` +### 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=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, 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) + +- 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) +- Criterion 2 (Operational Model): Fargate (managed containers) +- → **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. + +**Deterministic (fast-path) example:** + ```json { "gcp_type": "google_cloud_run_service", @@ -136,15 +181,40 @@ Apply in order; first match wins: "target_architecture": "arm64", "caveats": [] }, + "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": "t4g.medium", + "region": "us-east-1" + }, + "graviton": { + "compatibility": "ready", + "target_architecture": "arm64", + "caveats": [] + }, "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/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..cb3c9076 --- /dev/null +++ b/migrate/plugins/migration-to-aws/skills/gcp-to-aws/references/design-refs/elastic-beanstalk.md @@ -0,0 +1,234 @@ +# Elastic Beanstalk Design Reference + +**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 (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. + +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). +- "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 **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) +- 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 + +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 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 + +- **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 + +**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: + +- **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) | + +**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:** + +- 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, **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:** + +- 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` | 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 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 (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 `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 + +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.*`): + +| 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`) | + +**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`, `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` (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": "google_app_engine_application.example#default", + "gcp_config": { + "source_service": "default", + "runtime": "python312", + "instance_class": "F4", + "scaling": "automatic_scaling" + }, + "aws_service": "Elastic Beanstalk", + "aws_config": { + "eb_application": "example", + "eb_environment": "default", + "source_service": "default", + "platform": "Python 3.12 running on 64bit Amazon Linux 2023", + "environment_type": "LoadBalanced", + "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", + "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": "google_app_engine_application.example#worker", + "gcp_config": { + "source_service": "worker", + "runtime": "python312", + "instance_class": "B2", + "scaling": "manual_scaling(instances=1)" + }, + "aws_service": "Elastic Beanstalk", + "aws_config": { + "eb_application": "example", + "eb_environment": "worker", + "source_service": "worker", + "platform": "Python 3.12 running on 64bit Amazon Linux 2023", + "environment_type": "SingleInstance", + "instance_type": "t4g.small", + "min_instances": 1, + "max_instances": 1, + "region": "us-east-1" + }, + "graviton": { "compatibility": "ready", "target_architecture": "arm64", "caveats": [] }, + "confidence": "deterministic", + "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 ccadd150..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,21 +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 | +| 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 @@ -71,26 +72,37 @@ 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 | +| 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, `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 | -| 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). + +**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`. ## 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..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` | Fargate or Amplify | +| 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 78c3964e..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 @@ -31,7 +31,44 @@ 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`) 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. + +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. + +_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)._ --- 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 adf17484..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 @@ -13,7 +13,7 @@ The question catalog spans **six named categories (A–F)** plus agentic (G) and | File | Category | Questions | Loaded When | | --------------------- | ----------------------------------------- | --------- | ----------------------------------------------- | | `clarify-global.md` | A — Global/Strategic | Q1–Q7 | Always | -| `clarify-compute.md` | B — Config Gaps, C — Compute | Q8–Q11b | Compute or billing-source resources present | +| `clarify-compute.md` | B — Config Gaps, C — Compute | Q7b–Q11b | Compute or billing-source resources present | | `clarify-database.md` | D — Database | Q12–Q13b | Database resources present | | `clarify-ai.md` | F — AI/Bedrock, G — Agentic, H — Programs | Q14–Q27 | `ai-workload-profile.json` exists | | `clarify-ai-only.md` | _(standalone)_ | Q1–Q10 | AI-only migration (no infrastructure artifacts) | @@ -229,18 +229,18 @@ Record all extracted values in `metadata.inventory_clarifications` where applica ## Step 3: Question Disposition Catalog -### Category Firing Rules (unchanged) +### Category Firing Rules -| Category | Name | Firing Rule | Reference File | Questions | -| -------- | ------------------ | ------------------------------------------------------------------------------ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| **A** | Global/Strategic | **Always fires** | `clarify-global.md` | Q1 (location), Q2 (compliance), Q3 (GCP spend), Q3.5 (CUDs), Q4 (skipped), 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), Q11b (Graviton/ARM64 — see decision table; auto-defaults `cpu_architecture` when not asked) | -| **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–Q22 | -| **G** | Agentic | `agentic_profile.is_agentic == true` | `clarify-ai.md` | Q23–Q26 | -| **H** | Startup Programs | Fires with Category F | `clarify-ai.md` | Q27 | +| Category | Name | Firing Rule | Reference File | Questions | +| -------- | ------------------ | ------------------------------------------------------------------------------ | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **A** | Global/Strategic | **Always fires** | `clarify-global.md` | Q1 (location), Q2 (compliance), Q3 (GCP spend), Q3.5 (CUDs), Q4 (skipped), 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, App Engine) | `clarify-compute.md` | Q7b (compute model), Q8 (K8s sentiment), Q9 (WebSocket), Q10 (Cloud Run traffic), Q11 (Cloud Run spend), Q11b (Graviton/ARM64 — see decision table; auto-defaults `cpu_architecture` when not asked) | +| **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–Q22 | +| **G** | Agentic | `agentic_profile.is_agentic == true` | `clarify-ai.md` | Q23–Q26 | +| **H** | Startup Programs | Fires with Category F | `clarify-ai.md` | Q27 | **If no IaC, billing data, or code is available** (empty discovery): only Category A is active. All service-specific categories are skipped. @@ -278,6 +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 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**) | @@ -308,7 +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). +- **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. @@ -377,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`. @@ -492,45 +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 | -| 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 | --- @@ -594,6 +599,14 @@ If `preferences-draft.json` exists, use it as the base — merge in the final an "design_consequence": "Weekly maintenance window sets phased cutover timing in the migration plan", "question_id": "Q7" }, + "compute_model": { + "value": "managed_platform", + "chosen_by": "default", + "source": "default:Q7b", + "prompt": "What compute operational model do you prefer for your App Engine workloads? (default applied)", + "design_consequence": "Assuming managed platform → App Engine maps to Elastic Beanstalk", + "question_id": "Q7b" + }, "kubernetes": { "value": "ecs-fargate", "chosen_by": "default", @@ -765,6 +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, 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 0aa33261..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 | Fargate | Amplify, Lambda | +| `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 | @@ -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 9625de25..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 @@ -24,9 +24,20 @@ 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"`) **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; 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. + 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`.** 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). **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**. @@ -58,7 +69,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 @@ -81,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), **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, `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 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 f289d903..599026d7 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 @@ -119,6 +119,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/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 73599797..559d58af 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 @@ -159,17 +159,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. @@ -204,6 +205,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 | @@ -334,7 +336,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 533d91a9..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 @@ -36,6 +36,23 @@ Linux, on-demand. ARM64 (Graviton) is ~20% below x86 per AWS Fargate pricing. De arm64 (Graviton) is ~20% below x86 per GB-second; per-request price is the same for both architectures. +### 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: + +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 | Metric | Rate | 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 d59a1f02..8e01a1a7 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",