Installation

Install with CLI Recommended
gh skills-hub install azure-upgrade

Don't have the extension? Run gh extension install samueltauil/skills-hub first.

Download and extract to your repository:

.github/skills/azure-upgrade/

Extract the ZIP to .github/skills/ in your repo. The folder name must match azure-upgrade for Copilot to auto-discover it.

Skill Files (31)

SKILL.md 6.0 KB
---
name: azure-upgrade
description: "Assess and upgrade Azure workloads between plans, tiers, or SKUs, or modernize Azure SDK dependencies in source code. WHEN: upgrade Consumption to Flex Consumption, upgrade Azure Functions plan, change hosting plan, function app SKU, migrate App Service to Container Apps, modernize legacy Azure Java SDKs (com.microsoft.azure to com.azure), migrate Azure Cache for Redis (ACR/ACRE) to Azure Managed Redis (AMR)."
license: MIT
compatibility: python3.10+
metadata:
  author: Microsoft
  version: "1.1.3"
---

# Azure Upgrade

> This skill handles **assessment and automated upgrades** of existing Azure workloads from one Azure service, hosting plan, or SKU to another β€” all within Azure. This includes plan/tier upgrades (e.g. Consumption β†’ Flex Consumption), cross-service migrations (e.g. App Service β†’ Container Apps), and SKU changes. It also covers **Azure SDK for Java source-code modernization** (e.g. legacy Java `com.microsoft.azure.*` β†’ modern `com.azure.*`). This is NOT for cross-cloud migration β€” use `azure-cloud-migrate` for that.

## Triggers

| User Intent | Example Prompts |
|-------------|-----------------|
| Upgrade Azure Functions plan | "Upgrade my function app from Consumption to Flex Consumption" |
| Change hosting tier | "Move my function app to a better plan" |
| Assess upgrade readiness | "Is my function app ready for Flex Consumption?" |
| Automate plan migration | "Automate the steps to upgrade my Functions plan" |
| Modernize legacy Azure Java SDK | "Migrate legacy Azure SDKs for Java", "Upgrade legacy Azure Java SDK", "Migrate my Java project from com.microsoft.azure to com.azure" |
| Migrate Azure Cache for Redis (ACR/OSS) to Azure Managed Redis (AMR) | "Migrate my Redis cache to AMR", "ACR to AMR", "OSS to AMR", "Upgrade my Premium P2 cache to Managed Redis", "Pick an AMR SKU", "Convert my Redis IaC template to AMR" |
| Migrate Azure Cache for Redis Enterprise (ACRE) to Azure Managed Redis (AMR) | "Migrate my Enterprise_E10 cache to AMR", "ACRE to AMR", "Update my ACRE IaC template for AMR", "Migrate EnterpriseFlash to AMR", "Migrate my geo-replicated Enterprise Redis" |

## Rules

1. Follow phases sequentially β€” do not skip
2. Generate an assessment before any upgrade operations
3. Load the scenario reference and follow its rules
4. Use `mcp_azure_mcp_get_azure_bestpractices` and `mcp_azure_mcp_documentation` MCP tools
5. Destructive actions require `ask_user` β€” [global-rules](references/global-rules.md)
6. Always confirm the target plan/SKU with the user before proceeding
7. Never delete or stop the original app without explicit user confirmation
8. All automation scripts must be idempotent and resumable

## Upgrade Scenarios

| Source | Target | Reference |
|--------|--------|-----------|
| Azure Functions Consumption Plan | Azure Functions Flex Consumption Plan | [consumption-to-flex.md](references/services/functions/consumption-to-flex.md) |
| Legacy Azure Java SDK (`com.microsoft.azure.*`) | Modern Azure Java SDK (`com.azure.*`) | [languages/java/README.md](references/languages/java/README.md) |
| Azure Cache for Redis (ACR/OSS) Basic/Standard/Premium | Azure Managed Redis (AMR) | [services/redis/redis-to-amr.md](references/services/redis/redis-to-amr.md) |
| Azure Cache for Redis Enterprise (ACRE) / Enterprise Flash | Azure Managed Redis (AMR) | [services/redis/redis-to-amr.md](references/services/redis/redis-to-amr.md) |

> SDK upgrade scenarios (e.g. Java legacy β†’ modern) run a **source-code modernization flow** that is distinct from Azure service/plan/SKU upgrades: follow the scenario reference, **not** the Steps below.

> No matching scenario? Use `mcp_azure_mcp_documentation` and `mcp_azure_mcp_get_azure_bestpractices` tools to research the upgrade path.

## MCP Tools

| Tool | Purpose |
|------|---------|
| `mcp_azure_mcp_get_azure_bestpractices` | Get Azure best practices for the target service |
| `mcp_azure_mcp_documentation` | Look up Azure documentation for upgrade scenarios |
| `mcp_azure_mcp_appservice` | Query App Service and Functions plan details |
| `mcp_azure_mcp_applicationinsights` | Verify monitoring configuration |

## Steps

1. **Identify** β€” Determine the source and target Azure plans/SKUs. Ask user to confirm.
2. **Assess** β€” Analyze existing app for upgrade readiness β†’ load scenario reference (e.g., [consumption-to-flex.md](references/services/functions/consumption-to-flex.md))
3. **Pre-migrate** β€” Collect settings, identities, configs from the existing app
4. **Upgrade** β€” Execute the automated upgrade steps (create new resources, migrate settings, deploy code)
5. **Validate** β€” Hit the function app default URL to confirm the app is reachable, then verify endpoints and monitoring
6. **Ask User** β€” "Upgrade complete. Would you like to verify performance, clean up the old app, or update your IaC?"
7. **Hand off** to `azure-validate` for deep validation or `azure-deploy` for CI/CD setup

Track progress in `upgrade-status.md` inside the workspace root.

## References

- [Global Rules](references/global-rules.md)
- [Workflow Details](references/workflow-details.md)
- **Functions**
  - [Consumption to Flex Consumption](references/services/functions/consumption-to-flex.md)
  - [Assessment](references/services/functions/assessment.md)
  - [Automation Scripts](references/services/functions/automation.md)
- **Redis**
  - [Redis (ACR or ACRE) to AMR Migration](references/services/redis/redis-to-amr.md) β€” routes to dedicated [amr-migration-skill](https://github.com/AzureManagedRedis/amr-migration-skill) (ACR/OSS) or [acre-to-amr-migration-skill](https://github.com/AzureManagedRedis/acre-to-amr-migration-skill) (Enterprise)
- **Java SDK Migration Templates**
  - [Plan Template](references/languages/java/templates/PLAN_TEMPLATE.md)
  - [Progress Template](references/languages/java/templates/PROGRESS_TEMPLATE.md)
  - [Summary Template](references/languages/java/templates/SUMMARY_TEMPLATE.md)

## Next

After upgrade is validated, hand off to:
- `azure-validate` β€” for thorough post-upgrade validation
- `azure-deploy` β€” if the user wants to set up CI/CD for the new app
references/
global-rules.md 2.0 KB
# Global Rules

These rules apply to ALL phases of the azure-upgrade skill.

## Destructive Action Policy

β›” **NEVER** perform destructive actions without explicit user confirmation via `ask_user`:
- Deleting apps, services, or resource groups
- Stopping or disabling the original app/service
- Overwriting app settings or configuration in the new app
- Removing the original hosting plan or service tier
- Modifying DNS or custom domain bindings

## User Confirmation Required

Always use `ask_user` before:
- Selecting target Azure subscription
- Selecting target Azure region/location
- Creating new Azure resources
- Stopping or deleting the original app/service
- Modifying custom domains or network restrictions
- Any irreversible configuration change

## Best Practices

- Always use `mcp_azure_mcp_get_azure_bestpractices` tool before generating upgrade commands
- Prefer managed identity over connection strings β€” upgrades are a good time to improve security
- **Always target the latest supported runtime version** β€” check Azure docs for the newest GA version
- Keep the original app/service running until the upgraded one is fully validated
- Use the same resource group for the new resource to maintain access to existing dependencies
- Follow Azure naming conventions for all new resources

## Identity-First Authentication (Zero API Keys)

> Enterprise subscriptions commonly enforce policies that block local auth. Always design for identity-based access from the start.

- Prefer managed identity connections over connection strings/keys
- Use `DefaultAzureCredential` in code β€” works locally and in Azure
- When using User Assigned Managed Identity, always pass `managedIdentityClientId` explicitly
- See service-specific identity configuration in the scenario reference files

## Rollback Policy

- Always document rollback steps before executing upgrade
- Keep the original app intact and running until upgrade is validated
- If upgrade fails, guide the user to restart the original app
- Never delete the original app automatically β€” always require `ask_user`
workflow-details.md 1.6 KB
# Workflow Details

## Upgrade Workflow Phases

The azure-upgrade skill follows a structured workflow to ensure safe, repeatable upgrades.

## Phase Overview

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Identify │───▢│  Assess  │───▢│ Pre-migrate │───▢│ Upgrade │───▢│ Validate β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

## Progress Tracking

Create and maintain `upgrade-status.md` in the workspace root:

```markdown
# Upgrade Status

## Upgrade Details

| Property | Value |
|----------|-------|
| **Source App** | <app-name> |
| **Source Plan** | <current-plan> |
| **Target Plan** | <target-plan> |
| **Resource Group** | <resource-group> |
| **Region** | <region> |
| **Started** | <date> |

## Phase Status

- [ ] Phase 1: Identify β€” Determine source/target plans
- [ ] Phase 2: Assess β€” Check readiness and compatibility
- [ ] Phase 3: Pre-migrate β€” Collect settings and configurations
- [ ] Phase 4: Upgrade β€” Execute upgrade automation
- [ ] Phase 5: Validate β€” Verify new app functionality

## Notes

<any issues, decisions, or observations during upgrade>
```

## Error Handling

If any phase fails:
1. Log the error in `upgrade-status.md`
2. Do NOT proceed to the next phase
3. Inform the user of the failure and suggest remediation
4. Offer to retry the failed phase or rollback

## Hand-off

After successful validation:
- Offer to hand off to `azure-validate` for deeper testing
- Offer to hand off to `azure-deploy` for CI/CD pipeline setup
- Ask if the user wants to clean up the original app
references/languages/java/
INSTRUCTION.md 10.6 KB
# Azure SDK Migration Guidelines

## Context

The application is identified using legacy Azure SDKs for Java (`com.microsoft.azure.*`). These libraries reached end of support in 2023. They are not recommended for use in production, should be migrated to the latest Azure SDKs with the latest security patches and new capabilities support.

Follow these steps:

* **Inventory legacy dependencies**: Use tools such as `mvn dependency:tree` or `gradlew dependencies` to find every `com.microsoft.azure.*` SDK and map each one to its modern counterpart under `com.azure.*`. Do **not** rely solely on the root reactor β€” also grep the entire repository for legacy coordinates so you catch build files that aren't reachable from the root project. Run from the repo root:

  ```bash
  # Find every file referencing legacy groupIds/artifacts, including CI, samples, parent poms, buildSrc, version catalogs, Dockerfiles, and docs.
  grep -RIn --exclude-dir={.git,target,build,node_modules,out} \
    -E 'com\.microsoft\.azure(\.|:)|microsoft-azure-|azure-eventhubs-eph|azure-keyvault(:|["'\''])' .
  ```

  PowerShell equivalent (run from repo root):

  ```powershell
  Get-ChildItem -Path . -Recurse -File |
    Where-Object { $_.FullName -notmatch '(\\|/)(\.git|target|build|node_modules|out)(\\|/)' } |
    Select-String -Pattern 'com\.microsoft\.azure(\.|:)|microsoft-azure-|azure-eventhubs-eph|azure-keyvault(:|["''])'
  ```

  Commonly overlooked locations:`.ci/**/pom.xml`, `ci/**`, parent/BOM poms, `buildSrc/`, `gradle/libs.versions.toml`, `settings.gradle(.kts)`, `archetype-resources/`, sample sub-modules, Dockerfiles, shell/PowerShell scripts, and README snippets. Every hit must end up on the migration file list.

* **Adopt supported SDKs**: Replace the legacy dependencies with their modern equivalents in your `pom.xml` or `build.gradle`, following the migration guide to align feature parity and new SDK names.

* **Update application code**: Refactor your code to the builder-based APIs, updated authentication flows (Azure Identity), and modern async or reactive patterns required by the latest SDKs. Add concise comments explaining non-obvious changes.

* **Test thoroughly**: Run unit, integration, and end-to-end tests to validate that the modern SDKs behave as expected, focusing on authentication, retry, and serialization differences.

## Migration Guide

### Assumption

- Project is Maven or Gradle.
- Java code is on JDK 8 or above.

### Migrate dependencies

Use the latest stable `azure-sdk-bom` version from the Azure SDK for Java source of truth before editing any build file. Versions below `1.3.0` are invalid for this migration flow and must not be used.

Follow the detailed steps in [BOM Migration Guide](./bom-migration/bom-migration.md) β€” it covers how to determine the latest BOM version, plus Maven, plain Gradle, TOML version catalogs (`libs.versions.toml`), and programmatic version catalogs (`settings.gradle`).

### Migrate Java Code

- Make a list of source code/maven/gradle files that contains legacy SDK packages. Migrate each of them.
- Determine legacy SDK artifacts according to previous files, find suitable migration guides in [Package-Specific Migration Guides](#package-specific-migration-guides) and follow the guides whenever possible. Record which migration guide URL you used for each legacy package (e.g., in your plan or commit messages), so you can validate against them later.
- **Do not change the Java `package ...;` declaration at the top of each source file, and do not rename or move the source file's directory path to match a new SDK package structure.** Keep every `.java` file in its original directory; only update `import` statements and type usages inside the file body. For example, if a file lives in `src/main/java/com/microsoft/azure/eventprocessorhosts/Consumer.java` with `package com.microsoft.azure.eventprocessorhosts;`, it must stay in that exact directory and keep that exact package declaration β€” even though the modern SDK uses `com.azure.messaging.eventhubs`.
- Do not upgrade JDK version, if it is already JDK 8 or above.
- If there is test in the project, Java code there also need to be updated.

## Package-Specific Source Code Guidelines (Add them to plan guidelines when generating plan)

Use these package-specific references:

- [com.microsoft.azure.management.**](./package-specific/com.microsoft.azure.management.md)
- [com.microsoft.azure.eventprocessorhost](./package-specific/com.microsoft.azure.eventprocessorhost.md)

## Validation

**Make sure**
- Migrated project pass compilation.
- All tests pass. Don't silently skip tests.
- No legacy SDK dependencies/references exist. This is a **hard gate**, not a self-assessment β€” you must prove it by running the commands below from the repo root and showing they return zero hits. Do not declare migration complete until all three return empty:

  ```bash
  # 1. Legacy groupId / artifact references in ANY text file (pom.xml, *.gradle, *.gradle.kts, libs.versions.toml, Dockerfile, *.sh, *.md, etc.)
  grep -RIn --exclude-dir={.git,target,build,node_modules,out} \
    -E 'com\.microsoft\.azure(\.|:)|microsoft-azure-|azure-eventhubs-eph|azure-keyvault(:|["'\''])' .

  # 2. Legacy imports still in Java sources
  grep -RIn --include='*.java' -E '^\s*import\s+com\.microsoft\.azure\.' .

  # 3. Every pom.xml and *.gradle(.kts) file in the repo (not just the root reactor) β€” eyeball each for legacy coordinates
  find . -type d \( -name .git -o -name target -o -name build -o -name node_modules \) -prune -o \
    -type f \( -name 'pom.xml' -o -name '*.gradle' -o -name '*.gradle.kts' -o -name 'libs.versions.toml' \) -print
  ```

  PowerShell equivalent (run from repo root):

  ```powershell
  # 1. Legacy groupId / artifact references in ANY text file
  Get-ChildItem -Path . -Recurse -File |
    Where-Object { $_.FullName -notmatch '(\\|/)(\.git|target|build|node_modules|out)(\\|/)' } |
    Select-String -Pattern 'com\.microsoft\.azure(\.|:)|microsoft-azure-|azure-eventhubs-eph|azure-keyvault(:|["''])'

  # 2. Legacy imports still in Java sources
  Get-ChildItem -Path . -Recurse -File -Filter *.java |
    Where-Object { $_.FullName -notmatch '(\\|/)(\.git|target|build|node_modules|out)(\\|/)' } |
    Select-String -Pattern '^\s*import\s+com\.microsoft\.azure\.'

  # 3. Every pom.xml and *.gradle(.kts) file in the repo β€” eyeball each for legacy coordinates
  Get-ChildItem -Path . -Recurse -File -Include 'pom.xml','*.gradle','*.gradle.kts','libs.versions.toml' |
    Where-Object { $_.FullName -notmatch '(\\|/)(\.git|target|build|node_modules)(\\|/)' } |
    Select-Object -ExpandProperty FullName
  ```

  Pay special attention to files outside the root Maven/Gradle reactorβ€” e.g. `.ci/**/pom.xml`, `ci/**`, `buildSrc/`, sample sub-modules, archetype resources β€” these are frequently missed because `mvn dependency:tree` on the root project never visits them.
- If azure-sdk-bom is used, ensure **NO** explicit version dependencies for Azure libraries that are in azure-sdk-bom.
  E.g. Instead of `implementation 'com.azure.resourcemanager:azure-resourcemanager:2.60.0'`, we should use `implementation 'com.azure.resourcemanager:azure-resourcemanager'`.
  For Azure libraries in azure-sdk-bom, check https://raw.githubusercontent.com/Azure/azure-sdk-for-java/main/sdk/boms/azure-sdk-bom/pom.xml.
  The BOM version used during migration must be the latest stable version available at migration time and must not be below `1.3.0`.
  If the BOM version is below `1.3.0` or missing, or individual Azure packages still have explicit versions that should be managed by the BOM, follow the appropriate section in [BOM Migration Guide](./bom-migration/bom-migration.md) to fix it.
- **Version catalog projects**: Follow the [BOM Validation Checklist](./bom-migration/bom-validation.md) β€” it covers TOML, programmatic `settings.gradle` catalogs, and plain Gradle.
- For each migration guide you recorded during migration:
  1. Fetch and read the full content of the guide URL.
  2. Identify the migrated source files that correspond to that guide's package.
  3. Verify the migrated code follows the guide's recommended API replacements, class mappings, authentication patterns, and async/sync conventions.
  4. Fix any deviations β€” do not just report them.

## Package-Specific Migration Guides

- [Migrate to `com.azure.resourcemanager.**` from `com.microsoft.azure.management.**`](https://aka.ms/java-track2-migration-guide)
- [Migrate to com.azure:azure-messaging-servicebus from com.microsoft.azure:azure-servicebus](https://aka.ms/azsdk/java/migrate/sb)
- [Migrate to azure-messaging-eventhubs from azure-eventhubs and azure-eventhubs-eph](https://aka.ms/azsdk/java/migrate/eh)
- [Migrate to `azure-messaging-eventgrid` from `microsoft-azure-eventgrid`](https://aka.ms/azsdk/java/migrate/eg)
- [Storage Blob Service SDK Migration Guide from 8.x to 12.x](https://raw.githubusercontent.com/Azure/azure-sdk-for-java/main/sdk/storage/azure-storage-blob/migrationGuides/V8_V12.md)
- [Storage Blob Service SDK Migration Guide from 10.x/11.x to 12.x](https://raw.githubusercontent.com/Azure/azure-sdk-for-java/main/sdk/storage/azure-storage-blob/migrationGuides/V10_V12.md)
- [Storage Queue Service SDK Migration Guide from 8.x to 12.x](https://raw.githubusercontent.com/Azure/azure-sdk-for-java/main/sdk/storage/azure-storage-queue/migrationGuides/V8_V12.md)
- [Storage File Share Service SDK Migration Guide from 8.x to 12.x](https://raw.githubusercontent.com/Azure/azure-sdk-for-java/main/sdk/storage/azure-storage-file-share/migrationGuides/V8_V12.md)
- [Migrate to azure-security-keyvault-secrets from azure-keyvault](https://aka.ms/azsdk/java/migrate/kv-secrets)
- [Migrate to azure-security-keyvault-keys from azure-keyvault](https://aka.ms/azsdk/java/migrate/kv-keys)
- [Migrate to azure-security-keyvault-certificates from azure-keyvault](https://aka.ms/azsdk/java/migrate/kv-cert)
- [Migrate to `Azure-Compute-Batch` from `Microsoft-Azure-Batch`](https://raw.githubusercontent.com/Azure/azure-sdk-for-java/main/sdk/batch/azure-compute-batch/MigrationGuide.md)
- [Migrate to `azure-ai-documentintelligence` from `azure-ai-formrecognizer`](https://raw.githubusercontent.com/Azure/azure-sdk-for-java/main/sdk/documentintelligence/azure-ai-documentintelligence/MIGRATION_GUIDE.md)
- [Migrate to `azure-ai-formrecognizer 4.0.0-beta.1 - above` from `azure-ai-formrecognizer 3.1.x - lower`](https://raw.githubusercontent.com/Azure/azure-sdk-for-java/main/sdk/formrecognizer/azure-ai-formrecognizer/migration-guide.md)
- [Migration Guide from Azure OpenAI Java SDK to OpenAI Java SDK](https://raw.githubusercontent.com/Azure/azure-sdk-for-java/main/sdk/openai/azure-ai-openai-stainless/MIGRATION.md)
- [Migrate to azure-monitor-query from azure-loganalytics and azure-applicationinsights-query](https://aka.ms/azsdk/java/migrate/monitorquery)
README.md 3.0 KB
# Java Legacy Azure SDK β†’ Modern Azure SDK

> **Scenario scope**: Upgrade a Maven/Gradle project's Azure SDK dependencies from `com.microsoft.azure.*` (legacy, end-of-support 2023) to `com.azure.*` (modern) β€” source code, build files, tests.
>
> This is a **source-code modernization flow**, not an Azure service/plan/SKU upgrade. Follow the workflow below instead of the top-level `azure-upgrade` Steps. Do **NOT** use this for .NET, Python, JavaScript, or Go Azure SDK upgrades.

Upgrade all `com.microsoft.azure.*` to `com.azure.*` equivalents in one autonomous session.

You are an expert Azure SDK migration agent. Generate a unique run identifier at the start (format: `azure-sdk-upgrade-YYYYMMDD-HHMMSS`) and use it throughout all phases.

> ⚠️ **Lazy loading**: Do NOT pre-fetch the reference files listed below. Load each one **only when its workflow step is reached** or its trigger condition fires. Loading them upfront wastes context and causes premature decisions.

## Workflow (load references on demand)

Full procedure: per-phase files under `./workflow/` (load each one when entering that phase). Global rules apply to every step: [rules/execution-guidelines.md](./rules/execution-guidelines.md), [rules/efficiency.md](./rules/efficiency.md).

1. **Precheck** ([workflow/phase-1-precheck.md](./workflow/phase-1-precheck.md)) β€” Verify Maven/Gradle project, detect JDK/build tools. If git available, create branch `java-upgrade/{RUN_ID}`. β†’ load `./templates/PLAN_TEMPLATE.md` to create `plan.md`.
   - Step-wise rules: [rules/execution-guidelines.md](./rules/execution-guidelines.md) (Output directory, Git, Wrapper preference).
2. **Plan** ([workflow/phase-2-plan.md](./workflow/phase-2-plan.md)) β€” Inventory deps and populate `plan.md`. β†’ load `./INSTRUCTION.md` for package mappings.
   - Step-wise rules: [rules/upgrade-strategy.md](./rules/upgrade-strategy.md) (Incremental, Risk-first, Successor preference, Necessary/Meaningful steps).
3. **Execute** ([workflow/phase-3-execute.md](./workflow/phase-3-execute.md)) β€” Migrate build config then source, build/test/fix, commit per step. β†’ load `./templates/PROGRESS_TEMPLATE.md` to create `progress.md`; load `./rules/` before running builds/tests.
   - Step-wise rules: [rules/review-code-changes.md](./rules/review-code-changes.md), [rules/upgrade-strategy.md](./rules/upgrade-strategy.md) (Automation tools, Temporary errors OK), [rules/execution-guidelines.md](./rules/execution-guidelines.md) (Template compliance, Git).
4. **Validate** ([workflow/phase-4-summarize.md](./workflow/phase-4-summarize.md)) β€” Apply validation checklist. β†’ load `./templates/SUMMARY_TEMPLATE.md` to create `summary.md`; load `./INSTRUCTION.md#validation`.
   - Step-wise rules: [rules/upgrade-success-criteria.md](./rules/upgrade-success-criteria.md).

## Constraints

- 100% test pass Β· no premature termination Β· incremental changes Β· review each step
- Prefer wrappers (`mvnw`/`gradlew`)

## Examples

```
"upgrade legacy azure sdk" β†’ precheck β†’ plan β†’ execute β†’ validate
```
references/languages/java/bom-migration/
bom-gradle-settings.md 6.5 KB
# BOM Migration β€” Gradle Programmatic Version Catalog (`settings.gradle`)

Some projects define version catalogs programmatically in `settings.gradle` / `settings.gradle.kts` instead of using a TOML file. OpenRewrite does not support this either ([openrewrite/rewrite#4852](https://github.com/openrewrite/rewrite/issues/4852)). Handle manually.

## Step 0 β€” Detect programmatic catalog usage

Look for a `dependencyResolutionManagement` block in `settings.gradle` or `settings.gradle.kts`:

```groovy
// settings.gradle (Groovy DSL)
dependencyResolutionManagement {
    versionCatalogs {
        libs {
            version("azureSdk", "1.41.4")
            version("azureStorage", "8.6.6")
            library("azure", "com.microsoft.azure", "azure").versionRef("azureSdk")
            library("azure-storage", "com.microsoft.azure", "azure-storage").versionRef("azureStorage")
        }
    }
}
```

```kotlin
// settings.gradle.kts (Kotlin DSL)
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            version("azureSdk", "1.41.4")
            version("azureStorage", "8.6.6")
            library("azure", "com.microsoft.azure", "azure").versionRef("azureSdk")
            library("azure-storage", "com.microsoft.azure", "azure-storage").versionRef("azureStorage")
        }
    }
}
```

The `library()` call can also use the two-arg `module` form (the same syntax applies to Kotlin DSL):
```groovy
library("azure", "com.microsoft.azure:azure").versionRef("azureSdk")
library("azure-inline", "com.microsoft.azure:azure").version("1.41.4")
```

If the project has this pattern and contains Azure dependency entries, use this section. The same `libs.<alias>` accessor syntax is used in `build.gradle` as with TOML catalogs.

## Step 1 β€” Add or upgrade the BOM

Add a version and library entry for the BOM inside the `versionCatalogs` block:

```groovy
// Groovy DSL
dependencyResolutionManagement {
    versionCatalogs {
        libs {
            version("azureSdkBom", "{bom_version}")
            library("azure-sdk-bom", "com.azure", "azure-sdk-bom").versionRef("azureSdkBom")
        }
    }
}
```

```kotlin
// Kotlin DSL
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            version("azureSdkBom", "{bom_version}")
            library("azure-sdk-bom", "com.azure", "azure-sdk-bom").versionRef("azureSdkBom")
        }
    }
}
```

If a BOM entry already exists, update the version string.

Add the platform dependency (if not already present).

Groovy DSL (`build.gradle`):
```groovy
dependencies {
    implementation enforcedPlatform(libs.azure.sdk.bom)
}
```

Kotlin DSL (`build.gradle.kts`):
```kotlin
dependencies {
    implementation(enforcedPlatform(libs.azure.sdk.bom))
}
```

## Step 2 β€” Remove explicit versions from BOM-managed Azure libraries

For each modern Azure library (`com.azure.*`) managed by the BOM, change its catalog entry to remove the version. Use the `withoutVersion()` call:

The snippets below show Groovy DSL (`settings.gradle`); the same code applies to Kotlin DSL (`settings.gradle.kts`).

Before:
```groovy
library("azure-identity", "com.azure", "azure-identity").versionRef("azureIdentity")
```

After:
```groovy
library("azure-identity", "com.azure", "azure-identity").withoutVersion()
```

For the two-arg module form (the same syntax applies to Kotlin DSL):
```groovy
// Before
library("azure-identity", "com.azure:azure-identity").versionRef("azureIdentity")
// After
library("azure-identity", "com.azure:azure-identity").withoutVersion()
```

Then remove any orphaned `version(...)` calls that are no longer referenced.

## Step 3 β€” Replace legacy Azure library entries with modern equivalents

For each legacy `com.microsoft.azure.*` library call:
1. Replace the group and artifact with the modern `com.azure.*` equivalent.
2. Change `.versionRef(...)` or `.version(...)` to `.withoutVersion()` if the new artifact is managed by the BOM.
3. Remove orphaned `version(...)` calls.
4. If you rename the alias (first argument), update **all** references in `build.gradle` / `build.gradle.kts` and any `bundle(...)` calls.

The snippets below show Groovy DSL (`settings.gradle`); the same code applies to Kotlin DSL (`settings.gradle.kts`).

Before:
```groovy
version("azureSdk", "1.41.4")
version("azureStorage", "8.6.6")
library("azure", "com.microsoft.azure", "azure").versionRef("azureSdk")
library("azure-storage", "com.microsoft.azure", "azure-storage").versionRef("azureStorage")
```

After:
```groovy
library("azure-resourcemanager", "com.azure.resourcemanager", "azure-resourcemanager").withoutVersion()
library("azure-storage-blob", "com.azure", "azure-storage-blob").withoutVersion()
```

Then update `build.gradle` (Groovy DSL):
```groovy
// Before
implementation libs.azure
implementation libs.azure.storage

// After
implementation libs.azure.resourcemanager
implementation libs.azure.storage.blob
```

Or `build.gradle.kts` (Kotlin DSL):
```kotlin
// Before
implementation(libs.azure)
implementation(libs.azure.storage)

// After
implementation(libs.azure.resourcemanager)
implementation(libs.azure.storage.blob)
```

## Step 4 β€” Handle bundles

If `bundle(...)` calls reference any renamed or removed aliases, update them:

Groovy DSL (`settings.gradle`):
```groovy
// Before
bundle("azureLibs", ["azure", "azure-storage"])
// After
bundle("azureLibs", ["azure-resourcemanager", "azure-storage-blob", "azure-identity"])
```

Kotlin DSL (`settings.gradle.kts`):
```kotlin
// Before
bundle("azureLibs", listOf("azure", "azure-storage"))
// After
bundle("azureLibs", listOf("azure-resourcemanager", "azure-storage-blob", "azure-identity"))
```

## Expected settings.gradle after migration

Groovy DSL (`settings.gradle`):
```groovy
dependencyResolutionManagement {
    versionCatalogs {
        libs {
            version("azureSdkBom", "{bom_version}")
            library("azure-sdk-bom", "com.azure", "azure-sdk-bom").versionRef("azureSdkBom")
            library("azure-identity", "com.azure", "azure-identity").withoutVersion()
            library("azure-resourcemanager", "com.azure.resourcemanager", "azure-resourcemanager").withoutVersion()
        }
    }
}
```

Kotlin DSL (`settings.gradle.kts`):
```kotlin
dependencyResolutionManagement {
    versionCatalogs {
        create("libs") {
            version("azureSdkBom", "{bom_version}")
            library("azure-sdk-bom", "com.azure", "azure-sdk-bom").versionRef("azureSdkBom")
            library("azure-identity", "com.azure", "azure-identity").withoutVersion()
            library("azure-resourcemanager", "com.azure.resourcemanager", "azure-resourcemanager").withoutVersion()
        }
    }
}
```

bom-gradle-toml.md 2.6 KB
# BOM Migration β€” Gradle TOML Version Catalog (`libs.versions.toml`)

OpenRewrite does not yet support TOML version catalogs ([openrewrite/rewrite#4400](https://github.com/openrewrite/rewrite/issues/4400)). Handle manually.

Check for `gradle/libs.versions.toml`. If it exists and contains Azure entries, apply these steps. Always update `build.gradle` / `build.gradle.kts` references in tandem.

> πŸ’‘ **Tip:** TOML alias `azure-sdk-bom` becomes accessor `libs.azure.sdk.bom` (hyphensβ†’dots). CamelCase `azureSdkBom` becomes `libs.azureSdkBom`. Match the project's existing convention.

## Step 1 β€” Add or upgrade the BOM

In `gradle/libs.versions.toml`:
```toml
[versions]
azureSdkBom = "{bom_version}"

[libraries]
azure-sdk-bom = { group = "com.azure", name = "azure-sdk-bom", version.ref = "azureSdkBom" }
```

In `build.gradle` (Groovy DSL):
```groovy
implementation enforcedPlatform(libs.azure.sdk.bom)
```

In `build.gradle.kts` (Kotlin DSL):
```kotlin
implementation(enforcedPlatform(libs.azure.sdk.bom))
```

## Step 2 β€” Remove explicit versions from BOM-managed libraries

For each `com.azure.*` library in `[libraries]` that the BOM manages, drop `version.ref` / `version` and remove orphaned `[versions]` entries.

```toml
# Before
[versions]
azureIdentity = "1.15.0"
[libraries]
azureIdentity = { group = "com.azure", name = "azure-identity", version.ref = "azureIdentity" }

# After
[libraries]
azureIdentity = { group = "com.azure", name = "azure-identity" }
```

## Step 3 β€” Replace legacy entries with modern equivalents

For each `com.microsoft.azure.*` library: replace `group`/`name` with the modern `com.azure.*` equivalent, drop `version.ref`/`version` if BOM-managed, remove orphaned `[versions]` entries, and update alias references in `build.gradle` and `[bundles]`.

```toml
# Before
[versions]
azureSdk = "1.41.4"
azureStorage = "8.6.6"
[libraries]
azure = { group = "com.microsoft.azure", name = "azure", version.ref = "azureSdk" }
azureStorage = { group = "com.microsoft.azure", name = "azure-storage", version.ref = "azureStorage" }

# After (BOM-managed)
[libraries]
azureResourcemanager = { group = "com.azure.resourcemanager", name = "azure-resourcemanager" }
azureStorageBlob = { group = "com.azure", name = "azure-storage-blob" }
```

Update `build.gradle` and `[bundles]` to use the new aliases.

## TOML patterns to recognise

Libraries may use any of these forms β€” handle all of them:
```toml
lib = { group = "g", name = "a", version.ref = "v" }
lib = { module = "g:a", version.ref = "v" }
lib = { module = "g:a", version = "1.0" }
lib = { group = "g", name = "a", version = { strictly = "1.0" } }
```
bom-gradle.md 4.7 KB
# BOM Migration β€” Gradle Projects (No Version Catalogs)

> **Python availability**: The script below requires Python 3.8+. If `python3 --version` (or `python --version`) fails, skip the script section and follow [Manual Fallback (no Python)](#manual-fallback-no-python) instead.

## Automated (Python available)

Run the `upgrade_bom.py` script located at `references/languages/java/scripts/upgrade_bom.py` (relative to this skill). It auto-detects Gradle and performs:

1. **Set/upgrade the BOM** β€” adds `enforcedPlatform("com.azure:azure-sdk-bom:...")` if missing, or upgrades the version.
2. **Remove redundant explicit versions** β€” strips inline version strings from Azure dependencies managed by the BOM.

The following invocation works identically in **bash** and **PowerShell**:

```bash
# Path is relative to the skill directory (plugin/skills/azure-upgrade/)
python3 ./references/languages/java/scripts/upgrade_bom.py <project_dir> <bom_version>
```

Options:
- `--gradle <cmd>` β€” override the Gradle command (default: auto-detects `gradlew` or `gradle`).

Under the hood (OpenRewrite recipes):
- **Add BOM**: `AddPlatformDependency` ([docs](https://docs.openrewrite.org/recipes/gradle/addplatformdependency))
- **Upgrade BOM**: `UpgradeDependencyVersion` ([docs](https://docs.openrewrite.org/recipes/gradle/upgradedependencyversion))
- **Remove redundant versions**: `RemoveRedundantDependencyVersions` ([docs](https://docs.openrewrite.org/recipes/gradle/removeredundantdependencyversions))

> ⚠️ **Warning:** The script does **not** support Gradle version catalogs β€” neither TOML files nor programmatic `settings.gradle` catalogs. If the project uses either, follow [TOML catalog steps](./bom-gradle-toml.md) or [programmatic catalog steps](./bom-gradle-settings.md) instead.

## Expected build.gradle after migration

Groovy DSL (`build.gradle`):
```groovy
dependencies {
    implementation enforcedPlatform("com.azure:azure-sdk-bom:{bom_version}")

    implementation "com.azure:azure-identity"
    implementation "com.azure.resourcemanager:azure-resourcemanager"
}
```

Kotlin DSL (`build.gradle.kts`):
```kotlin
dependencies {
    implementation(enforcedPlatform("com.azure:azure-sdk-bom:{bom_version}"))

    implementation("com.azure:azure-identity")
    implementation("com.azure.resourcemanager:azure-resourcemanager")
}
```

## Manual Fallback (no Python)

When Python is unavailable, edit `build.gradle` (or `build.gradle.kts`) directly. Apply the same two steps as the script.

### Step 1 β€” Add or upgrade the BOM platform

Inside the `dependencies { }` block, add or update the `enforcedPlatform` line for `azure-sdk-bom`:

Groovy DSL:
```groovy
dependencies {
    implementation enforcedPlatform("com.azure:azure-sdk-bom:{bom_version}")
    // ...other dependencies...
}
```

Kotlin DSL:
```kotlin
dependencies {
    implementation(enforcedPlatform("com.azure:azure-sdk-bom:{bom_version}"))
    // ...other dependencies...
}
```

- **If the line exists**: update only the version to `{bom_version}`.
- **If the line is missing**: insert it at the top of the `dependencies` block.
- Use the same configuration (`implementation`, `api`, `compileOnly`, etc.) the project already uses for Azure deps. Repeat the platform line per configuration if needed.
- **Multi-project build**: add the platform line in every subproject that declares Azure dependencies, or apply it once via a shared `subprojects { }` / convention plugin.

### Step 2 β€” Remove redundant explicit versions

For every Azure dependency whose group starts with `com.azure` and is managed by the BOM (verify against `https://repo1.maven.org/maven2/com/azure/azure-sdk-bom/{bom_version}/azure-sdk-bom-{bom_version}.pom`), strip the version coordinate.

Groovy DSL β€” string notation:
```groovy
// Before
implementation "com.azure:azure-identity:1.13.0"
// After
implementation "com.azure:azure-identity"
```

Groovy DSL β€” map notation:
```groovy
// Before
implementation group: "com.azure", name: "azure-identity", version: "1.13.0"
// After
implementation group: "com.azure", name: "azure-identity"
```

Kotlin DSL:
```kotlin
// Before
implementation("com.azure:azure-identity:1.13.0")
// After
implementation("com.azure:azure-identity")
```

Do **not** strip versions from artifacts that are not managed by the BOM.

### Step 3 β€” Verify

Run the Gradle wrapper to inspect the resolved classpath. Use the form appropriate for your shell:

```bash
# bash / macOS / Linux
./gradlew dependencies --configuration runtimeClasspath
```

```powershell
# PowerShell on Windows
.\gradlew.bat dependencies --configuration runtimeClasspath
```

Then confirm:
- The platform `com.azure:azure-sdk-bom:{bom_version}` appears.
- All BOM-managed Azure artifacts resolve to versions sourced from the BOM.

Then continue with the validation checklist in [bom-validation.md](./bom-validation.md).
bom-maven.md 4.3 KB
# BOM Migration β€” Maven Projects

> **Python availability**: The script below requires Python 3.8+. If `python3 --version` (or `python --version`) fails, skip the script section and follow [Manual Fallback (no Python)](#manual-fallback-no-python) instead.

## Automated (Python available)

Run the `upgrade_bom.py` script located at `references/languages/java/scripts/upgrade_bom.py` (relative to this skill). It auto-detects Maven and performs two steps:

1. **Set/upgrade the BOM** β€” adds `azure-sdk-bom` if missing, or upgrades the version if already present.
2. **Remove redundant explicit versions** β€” strips explicit `<version>` tags from individual Azure dependencies that are now managed by the BOM.

The following invocation works identically in **bash** and **PowerShell**:

```bash
# Path is relative to the skill directory (plugin/skills/azure-upgrade/)
python3 ./references/languages/java/scripts/upgrade_bom.py <project_dir> <bom_version>
```

Options:
- `--mvn <cmd>` β€” override the Maven command (default: auto-detects `mvnw` or `mvn`).

Under the hood (OpenRewrite recipes):
- **Add BOM**: `AddManagedDependency` ([docs](https://docs.openrewrite.org/recipes/maven/addmanageddependency))
- **Upgrade BOM**: `UpgradeDependencyVersion` ([docs](https://docs.openrewrite.org/recipes/maven/upgradedependencyversion))
- **Remove redundant versions**: `RemoveRedundantDependencyVersions` ([docs](https://docs.openrewrite.org/recipes/maven/removeredundantdependencyversions))

## Expected pom.xml after migration

```xml
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.azure</groupId>
            <artifactId>azure-sdk-bom</artifactId>
            <version>{bom_version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>com.azure</groupId>
        <artifactId>azure-identity</artifactId>
    </dependency>
    <dependency>
        <groupId>com.azure.resourcemanager</groupId>
        <artifactId>azure-resourcemanager</artifactId>
    </dependency>
</dependencies>
```

## Manual Fallback (no Python)

When Python is unavailable, edit `pom.xml` directly. Apply the same two steps as the script:

### Step 1 β€” Add or upgrade `azure-sdk-bom`

Locate the `<dependencyManagement><dependencies>` block (create it inside `<project>` if absent). Add or update the BOM entry:

```xml
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.azure</groupId>
            <artifactId>azure-sdk-bom</artifactId>
            <version>{bom_version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!-- keep any other managed dependencies here -->
    </dependencies>
</dependencyManagement>
```

- **If the entry exists**: update only the `<version>` value to `{bom_version}`.
- **If the entry is missing**: insert the full `<dependency>` block above. Preserve any other existing managed dependencies.
- **Multi-module project**: add the BOM in the parent (aggregator) `pom.xml` only. Child modules inherit it.

### Step 2 β€” Remove redundant explicit versions

For every `<dependency>` whose `<groupId>` starts with `com.azure` (e.g. `com.azure`, `com.azure.resourcemanager`, `com.azure.spring`), check whether the BOM manages it (see the BOM POM at `https://repo1.maven.org/maven2/com/azure/azure-sdk-bom/{bom_version}/azure-sdk-bom-{bom_version}.pom`). If managed:

- Remove the `<version>` element entirely.
- Leave `<groupId>`, `<artifactId>`, `<scope>`, `<classifier>`, `<exclusions>`, etc. unchanged.

Before:
```xml
<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-identity</artifactId>
    <version>1.13.0</version>
</dependency>
```

After:
```xml
<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-identity</artifactId>
</dependency>
```

Do **not** strip versions from artifacts not managed by the BOM (verify each one against the BOM POM).

### Step 3 β€” Verify

Run `mvn -q -DskipTests dependency:tree` (the same command works in both **bash** and **PowerShell**) and confirm:
- `com.azure:azure-sdk-bom:pom:{bom_version}:import` appears in the managed dependencies.
- All BOM-managed Azure artifacts resolve to versions from `{bom_version}`.

Then continue with the validation checklist in [bom-validation.md](./bom-validation.md).
bom-migration.md 3.1 KB
# BOM Migration Guide

How to add or upgrade `azure-sdk-bom` and clean up redundant versions across all supported build configurations.

## Prerequisite β€” Python availability check

The Maven and plain-Gradle flows are automated by `scripts/upgrade_bom.py` (Python 3.8+). Before picking a guide, verify Python is available:

The following check works in both **bash** and **PowerShell 7+** (the `||` operator is supported in both):

```bash
python3 --version || python --version
```

For Windows PowerShell 5.1, use:

```powershell
python3 --version; if ($LASTEXITCODE -ne 0) { python --version }
```

- **Python available** β†’ use the script as documented in [bom-maven.md](./bom-maven.md) / [bom-gradle.md](./bom-gradle.md).
- **Python NOT available** β†’ follow the **Manual Fallback** section in the same guide. Do not attempt to install Python; perform the edits by hand.

The TOML and programmatic-catalog guides ([bom-gradle-toml.md](./bom-gradle-toml.md), [bom-gradle-settings.md](./bom-gradle-settings.md)) are manual-only and unaffected by Python availability.

## Determine the latest BOM version

Resolve the target `azure-sdk-bom` version from the Azure SDK for Java source of truth before editing build files. This is mandatory: do not hardcode, guess, or reuse an illustrative version from another example. Versions below `1.3.0` are invalid for this migration flow.

The following invocation works identically in **bash** and **PowerShell** (no shell-specific syntax):

```bash
# Path is relative to the skill directory (plugin/skills/azure-upgrade/)
python3 ./references/languages/java/scripts/upgrade_bom.py --get-latest-version
# or: python ./references/languages/java/scripts/upgrade_bom.py --get-latest-version
```

If Python is not available, fetch `https://raw.githubusercontent.com/Azure/azure-sdk-for-java/main/sdk/boms/azure-sdk-bom/pom.xml` directly and use the `<version>` value declared in that BOM `pom.xml`.

Do not continue until you have resolved that latest stable version explicitly.

## Decision Tree

```
Is the project Maven?
β”œβ”€ YES β†’ Maven projects (bom-maven.md)
└─ NO (Gradle)
     β”œβ”€ Does gradle/libs.versions.toml exist with Azure entries?
     β”‚    └─ YES β†’ TOML catalog steps (bom-gradle-toml.md)
     β”œβ”€ Does settings.gradle define a programmatic versionCatalogs block with Azure entries?
     β”‚    └─ YES β†’ Programmatic catalog steps (bom-gradle-settings.md)
     └─ Neither (plain build.gradle dependencies)
          └─ Plain Gradle projects (bom-gradle.md)
```

> πŸ’‘ **Tip:** To check which artifacts are managed by the BOM, fetch
> `https://repo1.maven.org/maven2/com/azure/azure-sdk-bom/{bom_version}/azure-sdk-bom-{bom_version}.pom`
> and look for `<dependency>` entries.

## Build-System Guides

| Build system | Guide |
|---|---|
| Maven | [bom-maven.md](./bom-maven.md) |
| Gradle (no version catalog) | [bom-gradle.md](./bom-gradle.md) |
| Gradle + TOML version catalog | [bom-gradle-toml.md](./bom-gradle-toml.md) |
| Gradle + programmatic catalog | [bom-gradle-settings.md](./bom-gradle-settings.md) |

## Validation

See the [Validation Checklist](./bom-validation.md) β€” covers all build systems including TOML and programmatic `settings.gradle` catalogs.
bom-validation.md 1.2 KB
# BOM Migration β€” Validation Checklist

After BOM migration, verify:

- [ ] Project compiles successfully.
- [ ] No legacy `com.microsoft.azure.*` dependencies remain anywhere (pom.xml, build.gradle, TOML, settings.gradle).
- [ ] BOM-managed Azure libraries have **no** explicit version (no `<version>` tag, no version string, no `version.ref`, no `.versionRef()`).
- [ ] The BOM version is correct β€” check against https://repo1.maven.org/maven2/com/azure/azure-sdk-bom/

## Additional checks for TOML version catalog projects

- [ ] No orphaned entries in `[versions]` (every version key must be referenced by at least one library or plugin).
- [ ] `[bundles]` aliases match current `[libraries]` aliases (no stale references).
- [ ] `build.gradle` uses `libs.<alias>` references β€” no raw `"group:artifact:version"` strings for Azure libraries.

## Additional checks for programmatic settings.gradle catalog projects

- [ ] No orphaned `version(...)` calls (every version must be referenced by at least one `library` or `plugin` entry).
- [ ] `bundle(...)` aliases match current `library(...)` aliases (no stale references).
- [ ] `build.gradle` uses `libs.<alias>` references β€” no raw `"group:artifact:version"` strings for Azure libraries.
references/languages/java/package-specific/
com.microsoft.azure.eventprocessorhost.md 5.5 KB
# com.microsoft.azure.eventprocessorhost

## Hard Rules (MUST follow)

- **MUST** replace any legacy `InMemoryCheckpointManager` / `InMemoryLeaseManager` and any `EventProcessorHost` checkpoint/lease usage with `BlobCheckpointStore` backed by Azure Blob Storage. This is the **only** acceptable replacement.
- **MUST NOT** create a class named `InMemoryCheckpointStore`, copy/inline `SampleCheckpointStore`, or implement any custom in-memory `CheckpointStore`. The modern SDK intentionally omits an in-memory implementation due to production issues.
- **MUST NOT** introduce any other custom `CheckpointStore` implementation as part of this migration. If the legacy code stored checkpoints in memory, switch to `BlobCheckpointStore` β€” do not preserve the in-memory semantics.
- If the user explicitly asks for an in-memory implementation, refuse and direct them to the upstream `SampleCheckpointStore` reference instead of generating one.

> ⚠️ **Important:** The switch from `InMemoryCheckpointManager`/`InMemoryLeaseManager` to `BlobCheckpointStore` is an **expected and necessary behavioral change**. It is inherent to the modern SDK design and **must not block the migration from completing**. Do not treat this behavioral difference as a migration defect or attempt to preserve in-memory semantics β€” proceed with `BlobCheckpointStore` and complete the migration.

## Code Samples

### EventProcessorHost with InMemoryCheckpointManager / InMemoryLeaseManager

Replace usage of `InMemoryCheckpointManager` / `InMemoryLeaseManager` with [BlobCheckpointStore](https://github.com/Azure/azure-sdk-for-java/blob/main/sdk/eventhubs/azure-messaging-eventhubs-checkpointstore-blob/src/main/java/com/azure/messaging/eventhubs/checkpointstore/blob/BlobCheckpointStore.java). The modern EventProcessorClient does **not** ship an in-memory checkpoint store, and you must not create one.

Legacy code:
```java
InMemoryCheckpointManager checkpointManager = new InMemoryCheckpointManager();
InMemoryLeaseManager leaseManager = new InMemoryLeaseManager();
EventProcessorHost host = new EventProcessorHost(
    hostName, eventHubName, consumerGroupName,
    eventHubConnectionString, checkpointManager, leaseManager);
host.registerEventProcessor(MyEventProcessor.class).get();
```

Migrated code β€” replace with `BlobCheckpointStore` backed by Azure Blob Storage:
```java
BlobContainerAsyncClient blobContainerClient = new BlobContainerClientBuilder()
    .connectionString(storageConnectionString)
    .containerName(storageContainerName)
    .buildAsyncClient();

// BlobCheckpointStore is the only supported replacement for InMemoryCheckpointManager/InMemoryLeaseManager.
// Do NOT create an InMemoryCheckpointStore or copy SampleCheckpointStore β€” there is no in-memory store in the modern SDK.
EventProcessorClient eventProcessorClient = new EventProcessorClientBuilder()
    .connectionString(eventHubConnectionString, eventHubName)
    .consumerGroup(consumerGroupName)
    .checkpointStore(new BlobCheckpointStore(blobContainerClient))
    .processEvent(eventContext -> {
        // Process event and checkpoint
        eventContext.updateCheckpoint();
    })
    .processError(errorContext -> {
        System.err.printf("Error in partition %s: %s%n",
            errorContext.getPartitionContext().getPartitionId(),
            errorContext.getThrowable().getMessage());
    })
    .buildEventProcessorClient();

eventProcessorClient.start();
```

> ⚠️ **Warning:** Add dependency `com.azure:azure-messaging-eventhubs-checkpointstore-blob` to the project when using `BlobCheckpointStore`.

### EventProcessorHost with Azure Storage checkpoint/lease

Legacy code using the built-in storage-backed checkpoint/lease:
```java
EventProcessorHost host = EventProcessorHost.EventProcessorHostBuilder
    .newBuilder(hostName, consumerGroupName)
    .useAzureStorageCheckpointLeaseManager(storageConnectionString, storageContainerName, null)
    .useEventHubConnectionString(eventHubConnectionString, eventHubName)
    .build();
host.registerEventProcessor(MyEventProcessor.class).get();
```

Migrated code:
```java
BlobContainerAsyncClient blobContainerClient = new BlobContainerClientBuilder()
    .connectionString(storageConnectionString)
    .containerName(storageContainerName)
    .buildAsyncClient();

EventProcessorClient eventProcessorClient = new EventProcessorClientBuilder()
    .connectionString(eventHubConnectionString, eventHubName)
    .consumerGroup(consumerGroupName)
    .checkpointStore(new BlobCheckpointStore(blobContainerClient))
    .processEvent(eventContext -> {
        // Process event and checkpoint
        eventContext.updateCheckpoint();
    })
    .processError(errorContext -> {
        System.err.printf("Error in partition %s: %s%n",
            errorContext.getPartitionContext().getPartitionId(),
            errorContext.getThrowable().getMessage());
    })
    .buildEventProcessorClient();

eventProcessorClient.start();
```

### Required imports for migrated code

```java
import com.azure.messaging.eventhubs.EventProcessorClient;
import com.azure.messaging.eventhubs.EventProcessorClientBuilder;
import com.azure.messaging.eventhubs.checkpointstore.blob.BlobCheckpointStore;
import com.azure.storage.blob.BlobContainerAsyncClient;
import com.azure.storage.blob.BlobContainerClientBuilder;
```

### Required dependencies

Add these dependencies when migrating from `com.microsoft.azure:azure-eventhubs-eph`:

| Legacy Artifact | Modern Artifact |
|---|---|
| `com.microsoft.azure:azure-eventhubs-eph` | `com.azure:azure-messaging-eventhubs` |
| (included in above) | `com.azure:azure-messaging-eventhubs-checkpointstore-blob` |
com.microsoft.azure.management.md 8.7 KB
# com.microsoft.azure.management.**

## Code Checklist

- Keep Azure resources, operations, and property values identical. The goal is functional equivalence, not feature expansion.
- Do not change the method sequence when creating or updating an Azure resource unless the new SDK requires it.
- Preserve the existing async pattern. For example, a delayed provisioning pattern that uses `Creatable<Resource>` should not be replaced by a direct `.create()` call. Similarly, when provisioning a resource, do not swap `.withNewDependencyResource` for `.withExistingDependencyResource` unless mandated by the new API surface.
- Keep the text emitted by logging and stdout/stderr unchanged to avoid breaking downstream consumers of those streams.
- Do not replace `resource.region()` with `resource.regionName()`; doing so changes the type from `Region` to `String` and can introduce subtle regressions.

## Code Samples

### Authentication with File

File-based authentication (e.g., `Azure.configure().authenticate(credentialFile)`) is **discouraged** under Azure's security-by-default posture: it relies on long-lived secrets stored on disk, which conflicts with the modern guidance to prefer managed identities, workload identity, or other credential types exposed by `DefaultAzureCredential`.

#### Detect: legacy file-based authentication patterns

Treat **any** of the following shapes in the legacy code as file-based authentication that must be replaced (not migrated). Triggers include `AZURE_AUTH_LOCATION`, `.authenticate(File)`, `ApplicationTokenCredentials.fromFile`, or any code path that reads `clientId` / `clientSecret` / `tenant` from disk and feeds them into a credential builder.

```java
// Shape A: direct File overload
Azure azure = Azure.authenticate(new File(System.getenv("AZURE_AUTH_LOCATION")))
                   .withDefaultSubscription();

// Shape B: configure() chain with a File
File credFile = new File(authFilePath);
Azure azure = Azure.configure()
    .withLogLevel(LogLevel.BASIC)
    .authenticate(credFile)
    .withDefaultSubscription();

// Shape C: ApplicationTokenCredentials.fromFile(...)
ApplicationTokenCredentials creds =
    ApplicationTokenCredentials.fromFile(new File(authFile));
Azure azure = Azure.authenticate(creds).withSubscription(subscriptionId);
```

Do **not** emit a code sample that reproduces any of the above during the upgrade β€” including a "modernized" variant that parses the same file with Jackson and feeds it into `ClientSecretCredentialBuilder`. Instead, replace the authentication block in the migrated code with a `DefaultAzureCredential` (or another appropriate `TokenCredential`) and prepend a `TODO` comment that explains the change. For example:

```java
// TODO: The original code authenticated using a credential file (AZURE_AUTH_LOCATION),
// which is discouraged because it relies on long-lived secrets on disk and conflicts
// with Azure's security-by-default guidance. It has been replaced with
// DefaultAzureCredential. This change alters the authentication mechanism, so the
// resulting code path requires extra testing (local dev, CI, and target runtime
// identities) before it is considered production-ready.
TokenCredential credential = new DefaultAzureCredentialBuilder().build();
AzureProfile profile = new AzureProfile(AzureEnvironment.AZURE);
AzureResourceManager azure = AzureResourceManager.configure()
    .authenticate(credential, profile)
    .withDefaultSubscription();
```

Keep the `TODO` comment in the migrated source so reviewers and downstream maintainers are aware that the authentication mechanism changed and that the new code path has not been exercised by the original tests.

### OKHttp Interceptors

Legacy OKHttp `Interceptor` implementation classes should be migrated to `HttpPipelinePolicy` implementation classes. This is a two-step migration β€” both steps are required:

**Step 1: Convert each `Interceptor` subclass to an `HttpPipelinePolicy` subclass.** Rename the class from `XxxInterceptor` to `XxxPolicy` and reimplement its logic against the `HttpPipelinePolicy` interface (i.e. `process(HttpPipelineCallContext, HttpPipelineNextPolicy)`) instead of the OKHttp `Interceptor.intercept(Chain)` API.

**Step 2: Register every converted policy on the new manager builder via `.withPolicy(new XxxPolicy())`.** Each `withNetworkInterceptor(new XxxInterceptor())` call on the legacy `RestClient.Builder` must have a corresponding `.withPolicy(new XxxPolicy())` call on `AzureResourceManager.configure()` (or the equivalent `XxxManager.configure()`). Preserve the original ordering of the interceptors when registering the policies.

Do not skip Step 2: converting the class without wiring it into the manager builder silently drops the behavior.

1. Legacy code:
```java
RestClient.Builder builder = new RestClient.Builder()
    ...
    .withNetworkInterceptor(new ResourceGroupTaggingInterceptor())
    ...;

Azure.Authenticated azureAuthed = Azure.authenticate(builder.build(), subscriptionId, credentials.domain());
Azure azure = azureAuthed.withSubscription(subscriptionId);
```

2. Migrated code (note: `ResourceGroupTaggingPolicy` is the Step 1 conversion of `ResourceGroupTaggingInterceptor`, and it must be registered via `.withPolicy(...)` in Step 2):
```java
AzureResourceManager azureResourceManager = AzureResourceManager.configure()
    .withPolicy(new ResourceGroupTaggingPolicy())
    .authenticate(credential, profile)
    .withDefaultSubscription();
```

### ProviderRegistrationInterceptor

If legacy client(XXManager) initializes with `ProviderRegistrationInterceptor`, check whether this client is one of the premium ones:
- Azure
- AuthorizationManager
- CdnManager
- ComputeManager
- ContainerInstanceManager
- ContainerRegistryManager
- ContainerServiceManager
- CosmosDBManager
- DnsZoneManager
- EventHubManager
- KeyVaultManager
- MonitorManager
- MSIManager
- NetworkManager
- RedisManager
- ResourceManager
- SearchServiceManager
- ServiceBusManager
- SqlServerManager
- StorageManager
- TrafficManager

If not, add `ProviderRegistrationPolicy` when initializing the client. Otherwise, don't.

For each legacy client, add along with whether to initialize with `ProviderRegistrationPolicy`, to the generated plan guideline, and migrate accordingly.

1. Legacy client(not premium client):
```java
BatchManager batchManager = BatchManager.configure()
    .withLogLevel(LogLevel.BASIC)
    .withInterceptor(new ProviderRegistrationInterceptor(credentials))
    .authenticate(credentials, subscriptionId);
```
should be migrated to:
```java
BatchManager batchManager = BatchManager.configure()
    .withPolicy(new ProviderRegistrationPolicy())
    .withLogOptions(new HttpLogOptions().setLogLevel(HttpLogDetailLevel.BASIC))
    .authenticate(credential, profile);
```

2. Legacy client(premium clients):
```java
Azure azure = Azure.configure()
    .withInterceptor(new ProviderRegistrationInterceptor(credentials))
    .withLogLevel(LogLevel.BASIC)
    .authenticate(credentials)
    .withSubscription(subscriptionId);
```
should be migrated to:
```java
AzureResourceManager.configure()
    .withLogLevel(HttpLogDetailLevel.BASIC)
    .authenticate(credential, profile);
```

### BatchAccount

azure-resourcemanager-batch is no longer a premium/handwritten library. In BatchAccount, `withNewStorageAccount` should be replaced by `.withAutoStorage(new AutoStorageBaseProperties().withStorageAccountId(storageAccount.id()))`, while the `storageAccount` needs to be created separately.

Legacy code:
```java
BatchAccount batchAccount = azure.batchAccounts().define(batchAccountName)
    .withRegion(region)
    .withNewResourceGroup(rgName)
    .defineNewApplication(applicationName)
        .defineNewApplicationPackage(applicationPackageName)
        .withAllowUpdates(true)
        .withDisplayName(applicationDisplayName)
        .attach()
    .withNewStorageAccount(storageAccountName)
    .create();
```

Migrated:
```java
StorageAccount storageAccount = storageManager.storageAccounts()
    .define(storageAccountName)
    .withRegion(REGION)
    .withExistingResourceGroup(resourceGroup)
    .create();
BatchAccount account = batchManager.batchAccounts()
    .define(batchAccountName)
    .withRegion(REGION)
    .withExistingResourceGroup(resourceGroup)
    .withAutoStorage(new AutoStorageBaseProperties().withStorageAccountId(storageAccount.id()))
    .create();
// create application with batch account
application = batchManager.applications()
    .define(applicationName)
    .withExistingBatchAccount(resourceGroup, account.name())
    .withDisplayName(applicationDisplayName)
    .withAllowUpdates(true)
    .create();
applicationPackage = batchManager.applicationPackages()
    .define(applicationPackageName)
    .withExistingApplication(resourceGroup, batchAccountName, applicationName)
    .create();
```
references/languages/java/rules/
efficiency.md 0.3 KB
# Efficiency

- **Targeted reads**: Use `grep` over full file reads; read sections, not entire files.
- **Quiet commands**: Use `-q`, `--quiet` for build/test when appropriate.
- **Progressive writes**: Update `plan.md` and `progress.md` incrementally, not at end.
execution-guidelines.md 1.0 KB
# Execution Guidelines

- **Wrapper preference**: Use Maven Wrapper (`mvnw`/`mvnw.cmd`) or Gradle Wrapper (`gradlew`/`gradlew.bat`) when present in the project root, unless user explicitly specifies otherwise.
- **Template compliance**: Follow the HTML-comment instructions in the template reference files when creating and populating `.github/java-upgrade/{RUN_ID}/plan.md`, `progress.md`, `summary.md`. You may remove the HTML comments after populating each section.
- **Output directory**: All plan/progress/summary files are created under `.github/java-upgrade/{RUN_ID}/` in the project being migrated. Create this directory at the start of the run.
- **Uninterrupted run**: Complete each phase fully without pausing for user input.
- **Git**: If git is available, create a new branch `java-upgrade/{RUN_ID}` before starting the migration. Commit changes per step on this branch. If git is not available, log a warning and proceed β€” files remain uncommitted in the working directory. Use `N/A` for `<current_branch>` and `<current_commit_id>` placeholders.
review-code-changes.md 0.5 KB
# Review Code Changes (MANDATORY for each step)

After completing changes in each step, review code changes per the rules in the Progress Template BEFORE verification. Key areas:

- **Sufficiency**: All required upgrade changes are present β€” no missing modifications that would leave the upgrade incomplete.
- **Necessity**: No critical unnecessary changes. Unnecessary changes that do not affect behavior may be retained; however, it is essential to ensure that functional behavior remains consistent and security controls are preserved.
troubleshooting.md 2.5 KB
# Troubleshooting & Anti-Excuse Rules

Load this file when encountering build failures, test failures, or any temptation to stop mid-migration.

## Anti-Excuse Rules (MANDATORY)

- **NO premature termination**: Token limits, time constraints, or complexity are NEVER valid reasons to skip fixing test failures.
- **NO "close enough" acceptance**: 95% is NOT 100%. Every failing test requires a fix attempt with documented root cause.
- **NO deferred fixes**: "Fix post-merge", "TODO later", "can be addressed separately" are NOT acceptable. Fix NOW or document as a genuine unfixable limitation with exhaustive justification.
- **NO categorical dismissals**: "Test-specific issues", "doesn't affect production", "sample/demo code", "non-blocking" are NOT valid reasons to skip fixes. ALL tests must pass.
- **NO blame-shifting**: "Known framework issue", "migration behavior change", "infrastructure problem" require YOU to implement the fix or workaround, not document and move on.
- **Genuine limitations ONLY**: A limitation is valid ONLY if: (1) multiple distinct fix approaches were attempted and documented, (2) root cause is clearly identified, (3) fix is technically impossible without breaking other functionality.

## Critical: Do Not Stop Mid-Migration

You are expected to carry the migration to completion β€” either fully succeed or encounter an unrecoverable error. The following behaviors are **strictly prohibited**:

- **Do NOT stop to summarize progress.** Never output a message listing "what was done" and "what remains" as your final response.
- **Do NOT treat partial migration as acceptable.** Migrating some files but not others is not a valid stopping point. You must attempt every file and every dependency.
- **Do NOT hand off work to the user.** Never suggest the user "continue" or "complete the remaining items." You are responsible for finishing everything.
- **If you hit an error on one file, move on to the next.** A failure in one source file should not prevent you from migrating the rest. Come back to fix it after attempting all files.
- **If a build fails after migration, debug and fix it.** Do not stop at "build failed." Investigate the errors, fix them, and rebuild. Repeat until the build passes or you have exhausted all reasonable approaches.

**The only acceptable stopping conditions are:**
1. The migration is fully complete and the build passes.
2. You have attempted everything and an unrecoverable error prevents further progress (e.g., a fundamental API incompatibility with no workaround). In this case, clearly state the blocker.
upgrade-strategy.md 1.3 KB
# Upgrade Strategy

- **Incremental upgrades**: Stepwise dependency upgrades to avoid large jumps breaking builds.
- **Minimal changes**: Only upgrade dependencies essential for compatibility with the modern Azure SDKs.
- **Risk-first**: Handle EOL/challenging deps early in isolated steps.
- **Necessary/Meaningful steps only**: Each step MUST change code/config. NO steps for pure analysis/validation. Merge small related changes. **Test**: "Does this step modify project files?"
- **Automation tools**: Use automation tools like OpenRewrite for efficiency; always verify output. For BOM upgrades, run the [`scripts/upgrade_bom.py`](../scripts/upgrade_bom.py) script in the parent folder when Python 3.8+ is available; if Python is not available, follow the **Manual Fallback** sections in [bom-maven.md](../bom-migration/bom-maven.md) / [bom-gradle.md](../bom-migration/bom-gradle.md) instead (see [Migration Guidelines](../INSTRUCTION.md#maven-use-the-upgrade_bom-script)).
- **Successor preference**: Compatible successor > Adapter pattern > Code rewrite.
- **Build tool compatibility**: Check Maven/Gradle version compatibility with the project's JDK. Upgrade the build tool (including wrapper) if the current version does not support the JDK.
- **Temporary errors OK**: Steps may pass with known errors if resolved later or pre-existing.
upgrade-success-criteria.md 0.7 KB
# Upgrade Success Criteria (ALL must be met)

- **Goal**: All legacy Azure SDK dependencies (`com.microsoft.azure.*`) replaced with modern equivalents (`com.azure.*`).
- **Compilation**: Both main source code AND test code compile successfully β€” `mvn clean test-compile` (or equivalent) succeeds.
- **Test**: **100% test pass rate** β€” `mvn clean test` succeeds. Minimum acceptable: test pass rate β‰₯ baseline (pre-upgrade pass rate). Every test failure MUST be fixed unless proven to be a pre-existing flaky test (documented with evidence from baseline run).

If any criterion is not met, load [`./troubleshooting.md`](./troubleshooting.md) at that point β€” do NOT stop or defer.
references/languages/java/scripts/
upgrade_bom.py 18.0 KB
#!/usr/bin/env python3
"""Upgrade or add azure-sdk-bom in a Maven or Gradle project using OpenRewrite.

Detects the build system automatically and applies the appropriate OpenRewrite
recipes.  All operations use OpenRewrite β€” no manual XML/text manipulation.

Step 1 – Add or upgrade BOM:
- Maven (add):     org.openrewrite.maven.AddManagedDependency
- Maven (upgrade): org.openrewrite.maven.UpgradeDependencyVersion
- Gradle (add):    org.openrewrite.gradle.AddPlatformDependency
- Gradle (upgrade):org.openrewrite.gradle.UpgradeDependencyVersion

Step 2 – Remove redundant explicit versions:
- Maven:  org.openrewrite.maven.RemoveRedundantDependencyVersions
- Gradle: org.openrewrite.gradle.RemoveRedundantDependencyVersions

Usage:
    python3 upgrade_bom.py <project_dir> <bom_version> [options]
    python3 upgrade_bom.py --get-latest-version

Arguments:
    project_dir   Path to the project root (must contain pom.xml or build.gradle).
    bom_version   Target azure-sdk-bom version to apply. Resolve the latest stable version first.

Options:
    --mvn <cmd>     Maven command override.
    --gradle <cmd>  Gradle command override.
"""

from __future__ import annotations

import argparse
import os
import re
import stat
import subprocess
import sys
import textwrap
import urllib.error
import urllib.request
import xml.etree.ElementTree as ET

GROUP_ID = "com.azure"
ARTIFACT_ID = "azure-sdk-bom"
BOM_POM_URL = "https://raw.githubusercontent.com/Azure/azure-sdk-for-java/main/sdk/boms/azure-sdk-bom/pom.xml"
POM_NAMESPACE = {"m": "http://maven.apache.org/POM/4.0.0"}

# Maven constants
MVN_REWRITE_PLUGIN = "org.openrewrite.maven:rewrite-maven-plugin"
MVN_REWRITE_ARTIFACT_COORDS = "org.openrewrite:rewrite-maven"
MVN_UPGRADE_RECIPE = "org.openrewrite.maven.UpgradeDependencyVersion"
MVN_ADD_MANAGED_RECIPE = "org.openrewrite.maven.AddManagedDependency"
MVN_REMOVE_REDUNDANT_RECIPE = "org.openrewrite.maven.RemoveRedundantDependencyVersions"

# Gradle constants
GRADLE_UPGRADE_RECIPE = "org.openrewrite.gradle.UpgradeDependencyVersion"
GRADLE_ADD_PLATFORM_RECIPE = "org.openrewrite.gradle.AddPlatformDependency"
GRADLE_REMOVE_REDUNDANT_RECIPE = "org.openrewrite.gradle.RemoveRedundantDependencyVersions"
REWRITE_YML_NAME = "rewrite.yml"
GRADLE_PLUGIN_MARKER = "// --- openrewrite-upgrade-bom-plugin (auto-added, safe to remove) ---"

# ---------------------------------------------------------------------------
# Build-system detection
# ---------------------------------------------------------------------------

def _detect_build_system(project_dir: str) -> str:
    """Return 'maven' or 'gradle' depending on which build file is present."""
    if os.path.isfile(os.path.join(project_dir, "pom.xml")):
        return "maven"
    for name in ("build.gradle", "build.gradle.kts"):
        if os.path.isfile(os.path.join(project_dir, name)):
            return "gradle"
    return "unknown"


def _get_latest_bom_version() -> str:
    try:
        with urllib.request.urlopen(BOM_POM_URL) as response:
            pom_xml = response.read()
    except urllib.error.URLError as exc:
        raise SystemExit(f"Failed to download {BOM_POM_URL}: {exc}") from exc

    try:
        root = ET.fromstring(pom_xml)
    except ET.ParseError as exc:
        raise SystemExit(f"Failed to parse BOM pom.xml: {exc}") from exc

    version = root.findtext("m:version", namespaces=POM_NAMESPACE)
    if not version:
        raise SystemExit("Failed to find the azure-sdk-bom <version> in pom.xml")

    return version.strip()


# ---------------------------------------------------------------------------
# Maven helpers
# ---------------------------------------------------------------------------

def _detect_maven(project_dir: str) -> str:
    if sys.platform == "win32":
        wrapper = os.path.join(project_dir, "mvnw.cmd")
        # .cmd files on Windows are invoked by the shell; no executable bit needed.
        if os.path.isfile(wrapper):
            return wrapper
    else:
        wrapper = os.path.join(project_dir, "mvnw")
        if os.path.isfile(wrapper):
            if not os.access(wrapper, os.X_OK):
                # Wrapper exists but isn't executable (common after fresh clones
                # on filesystems that don't preserve the +x bit). Try to fix it.
                try:
                    mode = os.stat(wrapper).st_mode
                    os.chmod(wrapper, mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
                    print(f"[upgrade_bom] Added executable bit to {wrapper}.")
                except OSError as exc:
                    print(
                        f"[upgrade_bom] WARNING: mvnw exists at {wrapper} but is not "
                        f"executable and chmod failed ({exc}); falling back to 'mvn'.",
                        file=sys.stderr,
                    )
                    return "mvn"
            if os.access(wrapper, os.X_OK):
                return wrapper
    return "mvn"


def _has_maven_bom_entry(pom_path: str) -> bool:
    try:
        tree = ET.parse(pom_path)
    except ET.ParseError:
        return False
    ns = {"m": "http://maven.apache.org/POM/4.0.0"}
    for dep in tree.findall(".//m:dependencyManagement/m:dependencies/m:dependency", ns):
        gid = dep.find("m:groupId", ns)
        aid = dep.find("m:artifactId", ns)
        if gid is not None and aid is not None:
            if gid.text == GROUP_ID and aid.text == ARTIFACT_ID:
                return True
    for dep in tree.findall(".//dependencyManagement/dependencies/dependency"):
        gid = dep.find("groupId")
        aid = dep.find("artifactId")
        if gid is not None and aid is not None:
            if gid.text == GROUP_ID and aid.text == ARTIFACT_ID:
                return True
    return False


def _run_maven_recipe(mvn_cmd: str, project_dir: str, recipe: str, options: str) -> int:
    """Run an OpenRewrite recipe via the rewrite-maven-plugin."""
    cmd = [
        mvn_cmd, "-U",
        f"{MVN_REWRITE_PLUGIN}:run",
        f"-Drewrite.recipeArtifactCoordinates={MVN_REWRITE_ARTIFACT_COORDS}",
        f"-Drewrite.activeRecipes={recipe}",
        f"-Drewrite.options={options}",
    ]
    print(f"[upgrade_bom] Running: {' '.join(cmd)}")
    return subprocess.run(cmd, cwd=project_dir).returncode


def _handle_maven(project_dir: str, bom_version: str, mvn_cmd: str | None) -> int:
    pom_path = os.path.join(project_dir, "pom.xml")
    mvn = mvn_cmd or _detect_maven(project_dir)

    # Step 1: Add or upgrade the BOM
    if not _has_maven_bom_entry(pom_path):
        print("[upgrade_bom] No existing azure-sdk-bom entry found β€” adding via AddManagedDependency.")
        options = ",".join([
            f"groupId={GROUP_ID}",
            f"artifactId={ARTIFACT_ID}",
            f"version={bom_version}",
            "type=pom",
            "scope=import",
        ])
        rc = _run_maven_recipe(mvn, project_dir, MVN_ADD_MANAGED_RECIPE, options)
        if rc != 0:
            print(f"[upgrade_bom] ERROR: AddManagedDependency exited with code {rc}", file=sys.stderr)
            return rc
        print(f"[upgrade_bom] azure-sdk-bom {bom_version} added successfully.")
    else:
        print(f"[upgrade_bom] Existing azure-sdk-bom entry found β€” upgrading to {bom_version}.")
        options = ",".join([
            f"groupId={GROUP_ID}",
            f"artifactId={ARTIFACT_ID}",
            f"newVersion={bom_version}",
            "overrideManagedVersion=true",
        ])
        rc = _run_maven_recipe(mvn, project_dir, MVN_UPGRADE_RECIPE, options)
        if rc != 0:
            print(f"[upgrade_bom] ERROR: UpgradeDependencyVersion exited with code {rc}", file=sys.stderr)
            return rc
        print(f"[upgrade_bom] azure-sdk-bom upgraded to {bom_version} successfully.")

    # Step 2: Remove explicit versions from Azure deps managed by the BOM
    print("[upgrade_bom] Removing redundant explicit versions for Azure dependencies...")
    options = f"groupPattern={GROUP_ID}*,onlyIfManagedVersionIs=GTE"
    rc = _run_maven_recipe(mvn, project_dir, MVN_REMOVE_REDUNDANT_RECIPE, options)
    if rc != 0:
        print(f"[upgrade_bom] WARNING: RemoveRedundantDependencyVersions exited with code {rc}", file=sys.stderr)
    else:
        print("[upgrade_bom] Redundant explicit versions removed successfully.")
    return rc


# ---------------------------------------------------------------------------
# Gradle helpers
# ---------------------------------------------------------------------------

def _detect_gradle(project_dir: str) -> str:
    if sys.platform == "win32":
        wrapper = os.path.join(project_dir, "gradlew.bat")
        if os.path.isfile(wrapper):
            return wrapper
    else:
        wrapper = os.path.join(project_dir, "gradlew")
        if os.path.isfile(wrapper):
            if not os.access(wrapper, os.X_OK):
                try:
                    mode = os.stat(wrapper).st_mode
                    os.chmod(wrapper, mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
                    print(f"[upgrade_bom] Added executable bit to {wrapper}.")
                except OSError as exc:
                    print(
                        f"[upgrade_bom] WARNING: gradlew exists at {wrapper} but is not "
                        f"executable and chmod failed ({exc}); falling back to 'gradle'.",
                        file=sys.stderr,
                    )
                    return "gradle"
            if os.access(wrapper, os.X_OK):
                return wrapper
    return "gradle"


def _find_gradle_build_file(project_dir: str) -> str | None:
    for name in ("build.gradle", "build.gradle.kts"):
        path = os.path.join(project_dir, name)
        if os.path.isfile(path):
            return path
    return None


def _is_kotlin_dsl(build_file: str) -> bool:
    return build_file.endswith(".kts")


def _has_gradle_bom_entry(build_file: str) -> bool:
    """Check whether build.gradle already references azure-sdk-bom."""
    try:
        with open(build_file, "r", encoding="utf-8") as f:
            content = f.read()
    except OSError:
        return False
    return f"{GROUP_ID}:{ARTIFACT_ID}" in content


def _create_rewrite_yml(project_dir: str, bom_version: str, has_bom: bool) -> str:
    """Create a temporary rewrite.yml with the appropriate OpenRewrite recipes.

    When has_bom is True, uses UpgradeDependencyVersion to upgrade the existing BOM.
    When has_bom is False, uses AddPlatformDependency to add a new enforcedPlatform BOM.
    Always includes RemoveRedundantDependencyVersions as a final step.
    """
    yml_path = os.path.join(project_dir, REWRITE_YML_NAME)
    recipes: list[str] = []

    if has_bom:
        recipes.append(textwrap.dedent(f"""\
          - org.openrewrite.gradle.UpgradeDependencyVersion:
              groupId: {GROUP_ID}
              artifactId: {ARTIFACT_ID}
              newVersion: {bom_version}"""))
    else:
        recipes.append(textwrap.dedent(f"""\
          - org.openrewrite.gradle.AddPlatformDependency:
              groupId: {GROUP_ID}
              artifactId: {ARTIFACT_ID}
              version: {bom_version}
              configuration: implementation
              enforced: true"""))

    recipes.append(textwrap.dedent(f"""\
          - org.openrewrite.gradle.RemoveRedundantDependencyVersions:
              groupPattern: {GROUP_ID}*
              onlyIfManagedVersionIs: GTE"""))

    yml_content = textwrap.dedent("""\
        ---
        type: specs.openrewrite.org/v1beta/recipe
        name: com.azure.UpgradeBom
        displayName: Upgrade azure-sdk-bom and remove redundant versions
        recipeList:
    """) + "\n".join(recipes) + "\n"

    with open(yml_path, "w", encoding="utf-8") as f:
        f.write(yml_content)
    print(f"[upgrade_bom] Created {yml_path}")
    return yml_path


def _inject_gradle_rewrite_plugin(build_file: str) -> bool:
    """Temporarily add the OpenRewrite plugin to build.gradle if not present.

    Returns True if the plugin block was injected (and should be cleaned up).
    """
    with open(build_file, "r", encoding="utf-8") as f:
        content = f.read()

    if "org.openrewrite.rewrite" in content:
        return False

    kotlin = _is_kotlin_dsl(build_file)
    if kotlin:
        plugin_line = '    id("org.openrewrite.rewrite") version "latest.release"'
    else:
        plugin_line = '    id "org.openrewrite.rewrite" version "latest.release"'

    rewrite_block_kt = textwrap.dedent("""\

        rewrite {
            activeRecipe("com.azure.UpgradeBom")
        }

        repositories {
            mavenCentral()
        }
    """)
    rewrite_block_groovy = rewrite_block_kt  # same syntax for both DSLs here

    plugins_pattern = re.compile(r"(plugins\s*\{)", re.MULTILINE)
    match = plugins_pattern.search(content)
    if match:
        insert_pos = match.end()
        # content[insert_pos:] already starts with the newline that follows
        # `plugins {`, so don't add another one before the marker.
        content = (
            content[:insert_pos]
            + "\n"
            + GRADLE_PLUGIN_MARKER
            + "\n"
            + plugin_line
            + content[insert_pos:]
        )
    else:
        # No plugins block β€” prepend one
        content = (
            "plugins {\n"
            + GRADLE_PLUGIN_MARKER
            + "\n"
            + plugin_line
            + "\n}\n\n"
            + content
        )

    content += GRADLE_PLUGIN_MARKER + "\n"
    content += rewrite_block_kt if kotlin else rewrite_block_groovy

    with open(build_file, "w", encoding="utf-8") as f:
        f.write(content)
    print(f"[upgrade_bom] Injected OpenRewrite plugin into {build_file}")
    return True


def _remove_gradle_rewrite_plugin(build_file: str) -> None:
    """Remove the temporarily injected OpenRewrite plugin and config blocks."""
    with open(build_file, "r", encoding="utf-8") as f:
        lines = f.readlines()

    cleaned: list[str] = []
    marker_count = 0
    i = 0
    while i < len(lines):
        line = lines[i]
        if GRADLE_PLUGIN_MARKER in line:
            marker_count += 1
            if marker_count == 1:
                # First marker (inside plugins {}): skip the marker line and
                # the following injected plugin id line.
                i += 2
                continue
            else:
                # Second marker (at end of file): skip the marker and every
                # remaining line β€” they're the injected rewrite {} and
                # repositories {} blocks.
                break
        cleaned.append(line)
        i += 1

    with open(build_file, "w", encoding="utf-8") as f:
        f.writelines(cleaned)
    print(f"[upgrade_bom] Cleaned up OpenRewrite plugin from {build_file}")


def _run_gradle_openrewrite(gradle_cmd: str, project_dir: str) -> int:
    cmd = [gradle_cmd, "rewriteRun"]
    print(f"[upgrade_bom] Running: {' '.join(cmd)}")
    return subprocess.run(cmd, cwd=project_dir).returncode


def _handle_gradle(project_dir: str, bom_version: str, gradle_cmd: str | None) -> int:
    build_file = _find_gradle_build_file(project_dir)
    if build_file is None:
        print("[upgrade_bom] ERROR: no build.gradle or build.gradle.kts found", file=sys.stderr)
        return 1

    gradle = gradle_cmd or _detect_gradle(project_dir)
    has_bom = _has_gradle_bom_entry(build_file)

    if has_bom:
        print(f"[upgrade_bom] Existing azure-sdk-bom entry found β€” upgrading to {bom_version}.")
    else:
        print("[upgrade_bom] No existing azure-sdk-bom entry found β€” adding via AddPlatformDependency.")

    yml_path = None
    injected = False

    try:
        # Set up OpenRewrite: create rewrite.yml + inject plugin temporarily
        yml_path = _create_rewrite_yml(project_dir, bom_version, has_bom=has_bom)
        injected = _inject_gradle_rewrite_plugin(build_file)
        rc = _run_gradle_openrewrite(gradle, project_dir)
    finally:
        if yml_path and os.path.isfile(yml_path):
            os.remove(yml_path)
            print(f"[upgrade_bom] Removed {yml_path}")
        if injected:
            _remove_gradle_rewrite_plugin(build_file)

    if rc != 0:
        print(f"[upgrade_bom] ERROR: OpenRewrite exited with code {rc}", file=sys.stderr)
    else:
        print(f"[upgrade_bom] BOM set to {bom_version} and redundant versions removed successfully.")
    return rc


# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------

def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(
        description="Upgrade azure-sdk-bom version in a Maven or Gradle project using OpenRewrite."
    )
    parser.add_argument("project_dir", nargs="?", help="Path to the project root.")
    parser.add_argument(
        "bom_version",
        nargs="?",
        help="Target azure-sdk-bom version to apply. Resolve the latest stable version first.",
    )
    parser.add_argument("--mvn", default=None, help="Maven command override.")
    parser.add_argument("--gradle", default=None, help="Gradle command override.")
    parser.add_argument(
        "--get-latest-version",
        action="store_true",
        help="Print the latest azure-sdk-bom version from the Azure SDK for Java BOM pom.xml.",
    )
    args = parser.parse_args(argv)

    if args.get_latest_version:
        if args.project_dir or args.bom_version:
            parser.error("--get-latest-version does not accept project_dir or bom_version.")
        print(_get_latest_bom_version())
        return 0

    if not args.project_dir or not args.bom_version:
        parser.error("project_dir and bom_version are required unless --get-latest-version is used.")

    project_dir = os.path.abspath(args.project_dir)
    build_system = _detect_build_system(project_dir)

    if build_system == "maven":
        print("[upgrade_bom] Detected Maven project.")
        return _handle_maven(project_dir, args.bom_version, args.mvn)
    elif build_system == "gradle":
        print("[upgrade_bom] Detected Gradle project.")
        return _handle_gradle(project_dir, args.bom_version, args.gradle)
    else:
        print(
            f"[upgrade_bom] ERROR: No pom.xml or build.gradle found in {project_dir}",
            file=sys.stderr,
        )
        return 1


if __name__ == "__main__":
    sys.exit(main())
references/languages/java/templates/
PLAN_TEMPLATE.md 9.4 KB
<!--
  This is the upgrade plan template for Azure SDK migration.
  RUN_ID should be replaced with the actual run identifier.

  ## PLANNING RULES

  !!! DON'T REMOVE THIS COMMENT BLOCK BEFORE FINAL PLAN IS GENERATED AS IT CONTAINS IMPORTANT INSTRUCTIONS.

  ### Strategy
  - **Incremental upgrades**: Stepwise dependency upgrades to avoid large jumps breaking builds
  - **Minimal changes**: Only upgrade dependencies essential for compatibility
  - **Risk-first**: Handle EOL/challenging deps early in isolated steps
  - **Necessary/Meaningful steps only**: Each step MUST change code/config. NO steps for pure analysis/validation. Merge small related changes.

  ### Mandatory Steps
  - **Step 1 (MANDATORY)**: Setup Baseline - Run compile/test with current JDK, document results.
  - **Steps 2-N**: Upgrade steps - dependency order, high-risk early, isolated breaking changes
  - **Final step (MANDATORY)**: Final Validation - verify all goals met, all TODOs resolved, 100% tests pass

  ### Verification Expectations
  - **Steps 1-N (Setup/Upgrade)**: Focus on COMPILATION SUCCESS. Tests may fail during intermediate steps.
  - **Final Validation**: COMPILATION SUCCESS + 100% TEST PASS

  ### Efficiency (IMPORTANT)
  - **Targeted reads**: Use `grep` over full file reads; read specific sections, not entire files. Template files are large - only read the sections you need.
  - **Quiet commands**: Use `-q`, `--quiet` for build/test commands when appropriate
  - **Progressive writes**: Update plan.md incrementally, not at end
-->

# Upgrade Plan: <PROJECT_NAME> (<RUN_ID>)

- **Generated**: <datetime> <!-- replace with actual date and time when generating -->
- **HEAD Branch**: <current_branch> <!-- replace with actual head branch when generating -->
- **HEAD Commit ID**: <current_commit_id> <!-- replace with actual head commit id when generating -->

## Available Tools

<!--
  List ONLY the JDKs and build tools that are required/used during the upgrade (not all discovered ones).
  Use the environment detection results from Precheck to check availability.
  Mark build tools that need upgrading for JDK compatibility as "**<TO_BE_UPGRADED>**".
  If a wrapper (mvnw/gradlew) is present, also check the wrapper-defined version in
  `.mvn/wrapper/maven-wrapper.properties` or `gradle/wrapper/gradle-wrapper.properties`.

  NOTE: This section is finalized during Upgrade Path Design (after step sequence is known), not during Environment Analysis.

  SAMPLE:
  **JDKs**
  - JDK 1.8.0: /path/to/jdk-8 (current project JDK, used by step 1)

  **Build Tools**
  - Maven 3.9.6: /path/to/maven
  - Maven Wrapper: 3.8.1 β†’ **<TO_BE_UPGRADED>** to 3.9.6+ (current version incompatible with project requirements)
-->

## Guidelines

<!--
  User-specified guidelines or constraints in bullet points for this upgrade.
  Extract these from the user's prompt if provided, or leave empty if none specified.
  These guidelines take precedence over default upgrade strategies.
-->

> Note: You can add any specific guidelines or constraints for the upgrade process here if needed, bullet points are preferred. <!-- this note is for users, NEVER remove it -->

## Upgrade Goals

<!--
  The primary goal is to replace all legacy Azure SDK dependencies (com.microsoft.azure.*) with their
  modern equivalents (com.azure.*). List any additional user-specified goals below.

  SAMPLE:
  - Replace all `com.microsoft.azure.*` dependencies with `com.azure.*` equivalents
  - Migrate source code to use modern Azure SDK APIs (builder pattern, Azure Identity)
-->

### Technology Stack

<!--
  Table of core dependencies and their compatibility with upgrade goals.
  IMPORTANT: Analyze ALL modules in multi-module projects, not just the root module.
  Only include: direct dependencies + those critical for upgrade compatibility.

  Columns:
  - Technology/Dependency: Name of the dependency
  - Current: Version currently in use
  - Modern Equivalent: The com.azure.* replacement (or N/A if not an Azure SDK dep)
  - Migration Notes: Notes on migration approach

  IMPORTANT: Include build tools (Maven/Gradle), wrappers, and key build plugins in this table.

  SAMPLE:
  | Technology/Dependency                    | Current | Modern Equivalent                          | Migration Notes                              |
  | ---------------------------------------- | ------- | ------------------------------------------ | -------------------------------------------- |
  | com.microsoft.azure:azure-mgmt-resources | 1.41.4  | com.azure.resourcemanager:azure-resourcemanager | Use azure-sdk-bom for version management |
  | com.microsoft.azure:azure-mgmt-storage   | 1.41.4  | com.azure.resourcemanager:azure-resourcemanager | Included in azure-resourcemanager        |
  | com.microsoft.azure:azure-client-authentication | 1.7.14 | com.azure:azure-identity             | Use ClientSecretCredential or DefaultAzureCredential |
  | Maven (wrapper)                          | 3.6.3   | -                                          | Check compatibility with project JDK         |
-->

| Technology/Dependency | Current | Modern Equivalent | Migration Notes |
| --------------------- | ------- | ----------------- | --------------- |

### Derived Upgrades

<!--
  Required upgrades inferred from the Azure SDK migration.
  Each derived upgrade must have a justification explaining WHY it's required.
  Common derivations:
  - Legacy auth (azure-client-authentication) β†’ azure-identity
  - Legacy management (azure-mgmt-*) β†’ azure-resourcemanager or specific azure-resourcemanager-* modules
  - Legacy data plane SDKs β†’ modern com.azure equivalents
  - Add azure-sdk-bom for version management

  SAMPLE:
  - Add azure-sdk-bom for centralized version management of com.azure.* dependencies
  - Replace azure-client-authentication with azure-identity (modern authentication library)
  - Add jackson-databind if file-based authentication is used and Jackson is not already present
-->

## Upgrade Steps

<!--
  Step-by-step upgrade plan. Each step should follow this format:
  - **Step N: <Descriptive Title>**
    - **Rationale**: Why this step is needed and why at this position
    - **Changes to Make**: ≀5 bullet points (concise)
    - **Verification**: Command, JDK, Expected Result

  VERIFICATION EXPECTATIONS:
  - Steps 1-N (Setup and Upgrade steps): Focus on COMPILATION SUCCESS. Tests may fail during intermediate steps.
  - Final step: COMPILATION SUCCESS + TEST PASS through iterative fix loop.

  MANDATORY FIRST STEP:
  The first step MUST always be Setup Baseline.

  MANDATORY SETUP BASELINE STEP SAMPLE:

  - Step 1: Setup Baseline
    - **Rationale**: Establish pre-upgrade compile and test results to measure upgrade success against.
    - **Changes to Make**:
      - [ ] Run baseline compilation with current JDK
      - [ ] Run baseline tests with current JDK
    - **Verification**:
      - Command: `mvn clean compile test-compile -q && mvn clean test -q`
      - JDK: <current project JDK path>
      - Expected: Document SUCCESS/FAILURE, test pass rate (forms acceptance criteria)

  ---

  SAMPLE STEP (dependency migration):

  - Step N: Migrate Azure Management Dependencies
    - **Rationale**: Replace legacy com.microsoft.azure management dependencies with modern com.azure.resourcemanager equivalents.
    - **Changes to Make**:
      - [ ] Add azure-sdk-bom to dependencyManagement
      - [ ] Replace com.microsoft.azure:azure-mgmt-* with com.azure.resourcemanager:azure-resourcemanager
      - [ ] Remove com.microsoft.azure:azure-client-authentication, add com.azure:azure-identity
      - [ ] Fix compilation errors from API changes
    - **Verification**:
      - Command: `mvn clean test-compile -q`
      - JDK: <JDK path>
      - Expected: Compilation SUCCESS (tests may fail - will be fixed in Final Validation)

  ---

  MANDATORY FINAL STEP (must always be the last step):

  - Step N: Final Validation
    - **Rationale**: Verify all upgrade goals met, project compiles successfully, all tests pass.
    - **Changes to Make**:
      - [ ] Verify no legacy com.microsoft.azure.* dependencies remain
      - [ ] Resolve ALL TODOs and temporary workarounds from previous steps
      - [ ] Clean rebuild with current JDK
      - [ ] Fix any remaining compilation errors
      - [ ] Run full test suite and fix ALL test failures (iterative fix loop until 100% pass)
    - **Verification**:
      - Command: `mvn clean test -q`
      - JDK: <JDK path>
      - Expected: Compilation SUCCESS + 100% tests pass
-->

## Key Challenges

<!--
  Document high-risk areas that require special attention during upgrade.
  Each challenge should have a mitigation strategy. Be concise.
  Common challenges for Azure SDK migration:
  - Authentication pattern changes (file-based auth, service principal)
  - API surface changes (fluent builder patterns differ between legacy and modern)
  - Async/reactive pattern differences
  - Package structure changes (com.microsoft.azure.management.* β†’ com.azure.resourcemanager.*)

  SAMPLE:
  - **Authentication Migration**
     - **Challenge**: Legacy code uses file-based authentication via Azure.authenticate(credentialFile). Modern SDK requires explicit credential construction.
     - **Strategy**: Read credential file with Jackson ObjectMapper, construct ClientSecretCredential, use AzureProfile for subscription context.
  - **Fluent API Changes**
     - **Challenge**: Method names and builder patterns differ between legacy and modern Azure Resource Manager.
     - **Strategy**: Follow the migration guide at https://aka.ms/java-track2-migration-guide for method-level mappings.
-->
PROGRESS_TEMPLATE.md 9.0 KB
<!--
  This is the upgrade progress tracker for Azure SDK migration.
  Each step from plan.md should be tracked here with status, changes, verification results, and TODOs.

  ## EXECUTION RULES

  !!! DON'T REMOVE THIS COMMENT BLOCK BEFORE UPGRADE IS COMPLETE AS IT CONTAINS IMPORTANT INSTRUCTIONS.

  ### Success Criteria
  - **Goal**: All legacy Azure SDK dependencies (com.microsoft.azure.*) replaced with modern equivalents (com.azure.*)
  - **Compilation**: Both main source code AND test code compile = `mvn clean test-compile` succeeds
  - **Test**: 100% test pass rate = `mvn clean test` succeeds (or β‰₯ baseline with documented pre-existing flaky tests)

  ### Strategy
  - **Uninterrupted run**: Complete execution without pausing for user input
  - **NO premature termination**: Token limits, time constraints, or complexity are NEVER valid reasons to skip fixing.
  - **Automation tools**: Use OpenRewrite etc. for efficiency; always verify output

  ### Verification Expectations
  - **Steps 1-N (Setup/Upgrade)**: Focus on COMPILATION SUCCESS (both main and test code).
    - On compilation success: Commit and proceed (even if tests fail - document count)
    - On compilation error: Fix IMMEDIATELY and re-verify until both main and test code compile
    - **NO deferred fixes** (for compilation): "Fix post-merge", "TODO later", "can be addressed separately" are NOT acceptable. Fix NOW or document as genuine unfixable limitation.
  - **Final Validation Step**: Achieve COMPILATION SUCCESS + 100% TEST PASS.
    - On test failure: Enter iterative test & fix loop until 100% pass or rollback to last-good-commit after exhaustive fix attempts
    - **NO deferring test fixes** - this is the final gate
    - **NO categorical dismissals**: "Test-specific issues", "doesn't affect production", "sample/demo code" are NOT valid reasons to skip. ALL tests must pass.
    - **NO "close enough" acceptance**: 95% is NOT 100%. Every failing test requires a fix attempt with documented root cause.
    - **NO blame-shifting**: "Known framework issue", "migration behavior change" require YOU to implement the fix or workaround.

  ### Review Code Changes (MANDATORY for each step)
  After completing changes in each step, review code changes BEFORE verification to ensure:

  1. **Sufficiency**: All changes required for the upgrade goal are present β€” no missing modifications that would leave the upgrade incomplete.
     - All dependencies/plugins listed in the plan for this step are updated
     - All required code changes (API migrations, import updates, config changes) are made
     - All compilation and compatibility issues introduced by the upgrade are addressed
  2. **Necessity**: All changes are strictly necessary for the upgrade β€” no unnecessary modifications, refactoring, or "improvements" beyond what's required. This includes:
     - **Functional Behavior Consistency**: Original code behavior and functionality are maintained:
       - Business logic unchanged
       - API contracts preserved (inputs, outputs, error handling)
       - Expected outputs and side effects maintained
     - **Security Controls Preservation** (critical subset of behavior):
       - **Authentication**: Login mechanisms, session management, token validation, MFA configurations
       - **Authorization**: Role-based access control, permission checks, access policies, security annotations (@PreAuthorize, @Secured, etc.)
       - **Password handling**: Password encoding/hashing algorithms, password policies, credential storage
       - **Security configurations**: CORS policies, CSRF protection, security headers, SSL/TLS settings, OAuth/OIDC configurations
       - **Audit logging**: Security event logging, access logging

  **Review Code Changes Actions**:
  - Review each changed file for missing upgrade changes, unintended behavior or security modifications
  - If behavior must change due to framework requirements, document the change, the reason, and confirm equivalent functionality/protection is maintained
  - Add missing changes that are required for the upgrade step to be complete
  - Revert unnecessary changes that don't affect behavior or security controls
  - Document review results in progress.md and commit message

  ### Commit Message Format
  - First line: `Step <x>: <title> - Compile: <result>` or `Step <x>: <title> - Compile: <result>, Tests: <pass>/<total> passed`
  - Body: Changes summary + concise known issues/limitations (≀5 lines)
  - **Security note**: If any security-related changes were made, include "Security: <change description and justification>"

  ### Efficiency (IMPORTANT)
  - **Targeted reads**: Use `grep` over full file reads; read specific sections, not entire files. Template files are large - only read the section you need.
  - **Quiet commands**: Use `-q`, `--quiet` for build/test commands when appropriate
  - **Progressive writes**: Update progress.md incrementally after each step, not at end
-->

# Upgrade Progress: <PROJECT_NAME> (<RUN_ID>)

- **Started**: <timestamp> <!-- replace with actual start timestamp -->
- **Plan Location**: `.github/java-upgrade/<RUN_ID>/plan.md`
- **Total Steps**: <number of steps from plan.md>

## Step Details

<!--
  For each step in plan.md, track progress using this bullet list format:

  - **Step N: <Step Title>**
    - **Status**: <status emoji>
      - πŸ”˜ Not Started - Step has not been started yet
      - ⏳ In Progress - Currently working on this step
      - βœ… Completed - Step completed successfully
      - ❗ Failed - Step failed after exhaustive attempts
    - **Changes Made**: (≀5 bullets, keep each ≀20 words)
      - Focus on what changed, not how
    - **Review Code Changes**:
      - Sufficiency: βœ… All required changes present / ⚠️ <list missing changes added, short and concise>
      - Necessity: βœ… All changes necessary / ⚠️ <list unnecessary changes reverted, short and concise>
        - Functional Behavior: βœ… Preserved / ⚠️ <list unavoidable changes with justification, short and concise>
        - Security Controls: βœ… Preserved / ⚠️ <list unavoidable changes with justification and equivalent protection, short and concise>
    - **Verification**:
      - Command: <actual command executed>
      - JDK: <JDK path used>
      - Build tool: <Path of build tool used>
      - Result: <SUCCESS/FAILURE with details>
      - Notes: <any skipped checks, excluded modules, known issues>
    - **Deferred Work**: List any deferred work, temporary workarounds (or "None")
    - **Commit**: <commit hash> - <commit message first line>

  ---

  SAMPLE UPGRADE STEP:

  - **Step X: Migrate Azure Management Dependencies**
    - **Status**: βœ… Completed
    - **Changes Made**:
      - Added azure-sdk-bom to dependencyManagement
      - Replaced com.microsoft.azure:azure-mgmt-* with com.azure.resourcemanager
      - Replaced azure-client-authentication with azure-identity
    - **Review Code Changes**:
      - Sufficiency: βœ… All required changes present
      - Necessity: βœ… All changes necessary
        - Functional Behavior: βœ… Preserved - API contracts and business logic unchanged
        - Security Controls: βœ… Preserved - authentication pattern updated with equivalent protection
    - **Verification**:
      - Command: `mvn clean test-compile -q`
      - JDK: /usr/lib/jvm/java-8-openjdk
      - Build tool: /usr/local/maven/bin/mvn
      - Result: βœ… Compilation SUCCESS | ⚠️ Tests: 10/12 passed (2 failures deferred to Final Validation)
      - Notes: 2 test failures related to auth mock setup
    - **Deferred Work**: Fix 2 test failures in Final Validation step (TestAuthHelper, TestResourceCreation)
    - **Commit**: abc1234 - Step X: Migrate Azure Management Dependencies - Compile: SUCCESS, Tests: 10/12 passed

  ---

  SAMPLE FINAL VALIDATION STEP:

  - **Step X: Final Validation**
    - **Status**: βœ… Completed
    - **Changes Made**:
      - Verified no legacy com.microsoft.azure.* dependencies remain
      - Resolved 2 TODOs from Step 3
      - Fixed 2 test failures (auth mock setup, resource assertion)
    - **Review Code Changes**:
      - Sufficiency: βœ… All required changes present
      - Necessity: βœ… All changes necessary
        - Functional Behavior: βœ… Preserved - all business logic and API contracts maintained
        - Security Controls: βœ… Preserved - all authentication, authorization unchanged
    - **Verification**:
      - Command: `mvn clean test -q`
      - JDK: /usr/lib/jvm/java-8-openjdk
      - Build tool: /usr/local/maven/bin/mvn
      - Result: βœ… Compilation SUCCESS | βœ… Tests: 12/12 passed (100% pass rate achieved)
    - **Deferred Work**: None - all TODOs resolved
    - **Commit**: xyz3456 - Step X: Final Validation - Compile: SUCCESS, Tests: 12/12 passed
-->

---

## Notes

<!--
  Additional context, observations, or lessons learned during execution.
  Use this section for:
  - Unexpected challenges encountered
  - Deviation from original plan
  - Performance observations
  - Recommendations for future upgrades

  SAMPLE:
  - azure-sdk-bom simplified version management significantly
  - Authentication migration required adding jackson-databind for file-based auth
  - Modern SDK fluent API method names differ from legacy β€” migration guide was essential
-->
SUMMARY_TEMPLATE.md 8.6 KB
<!--
  This is the upgrade summary generated after successful completion of the Azure SDK migration.
  It documents the final results, changes made, and lessons learned.

  ## SUMMARY RULES

  !!! DON'T REMOVE THIS COMMENT BLOCK BEFORE UPGRADE IS COMPLETE AS IT CONTAINS IMPORTANT INSTRUCTIONS.

  ### Prerequisites (must be met before generating summary)
  - All steps in plan.md have βœ… in progress.md
  - Final Validation step completed successfully

  ### Success Criteria Verification
  - **Goal**: All legacy Azure SDK dependencies (com.microsoft.azure.*) replaced with modern equivalents (com.azure.*)
  - **Compilation**: Both main AND test code compile = `mvn clean test-compile` succeeds
  - **Test**: 100% pass rate = `mvn clean test` succeeds (or β‰₯ baseline with documented pre-existing flaky tests)

  ### Content Guidelines
  - **Upgrade Result**: MUST show 100% pass rate or justify EACH failure with exhaustive documentation
  - **Tech Stack Changes**: Table with Dependency | Before | After | Reason
  - **Commits**: List with IDs and messages from each step
  - **Challenges**: Key issues and resolutions encountered
  - **Limitations**: Only genuinely unfixable items where: (1) multiple fix approaches attempted, (2) root cause identified, (3) technically impossible to fix
  - **Next Steps**: Recommendations for post-upgrade actions

  ### Efficiency (IMPORTANT)
  - **Targeted reads**: Use `grep` over full file reads; read specific sections from progress.md, not entire files.
-->

# Upgrade Summary: <PROJECT_NAME> (<RUN_ID>)

- **Completed**: <timestamp> <!-- replace with actual completion timestamp -->
- **Plan Location**: `plan.md`
- **Progress Location**: `progress.md`

## Upgrade Result

<!--
  Compare final compile/test results against baseline.
  MUST show 100% pass rate or justify EACH failure with exhaustive documentation.

  SAMPLE:
  | Metric     | Baseline           | Final              | Status |
  | ---------- | ------------------ | ------------------ | ------ |
  | Compile    | βœ… SUCCESS         | βœ… SUCCESS        | βœ…     |
  | Tests      | 12/12 passed       | 12/12 passed       | βœ…     |
  | JDK        | JDK 8              | JDK 8              | βœ…     |
  | Build Tool | Maven 3.9.6        | Maven 3.9.6        | βœ…     |

  **Upgrade Goals Achieved**:
  - βœ… All com.microsoft.azure.* dependencies replaced with com.azure.* equivalents
  - βœ… Source code migrated to modern Azure SDK APIs
-->

| Metric     | Baseline | Final | Status |
| ---------- | -------- | ----- | ------ |
| Compile    |          |       |        |
| Tests      |          |       |        |
| JDK        |          |       |        |
| Build Tool |          |       |        |

**Upgrade Goals Achieved**:

## Tech Stack Changes

<!--
  Table documenting all dependency changes made during the upgrade.
  Only include dependencies that were actually changed.

  SAMPLE:
  | Dependency                                      | Before  | After                                            | Reason                              |
  | ----------------------------------------------- | ------- | ------------------------------------------------ | ----------------------------------- |
  | com.microsoft.azure:azure-mgmt-resources        | 1.41.4  | Removed                                          | Replaced by azure-resourcemanager   |
  | com.azure.resourcemanager:azure-resourcemanager  | N/A     | (managed by azure-sdk-bom)                       | Modern replacement                  |
  | com.microsoft.azure:azure-client-authentication  | 1.7.14  | Removed                                          | Replaced by azure-identity          |
  | com.azure:azure-identity                         | N/A     | (managed by azure-sdk-bom)                       | Modern auth library                 |
  | com.azure:azure-sdk-bom                          | N/A     | 1.3.3                                            | Centralized version management      |
-->

| Dependency | Before | After | Reason |
| ---------- | ------ | ----- | ------ |

## Commits

<!--
  List all commits made during the upgrade with their short IDs and messages.

  SAMPLE:
  | Commit  | Message                                                                |
  | ------- | ---------------------------------------------------------------------- |
  | abc1234 | Step 1: Setup Baseline - Compile: SUCCESS \| Tests: 12/12 passed      |
  | def5678 | Step 2: Migrate Azure Dependencies - Compile: SUCCESS                  |
  | ghi9012 | Step 3: Migrate Source Code - Compile: SUCCESS                         |
  | jkl3456 | Step 4: Final Validation - Compile: SUCCESS \| Tests: 12/12 passed    |
-->

| Commit | Message |
| ------ | ------- |

## Challenges

<!--
  Document key challenges encountered during the upgrade and how they were resolved.

  SAMPLE:
  - **Authentication Migration**
    - **Issue**: Legacy code uses Azure.authenticate(credentialFile) which has no direct equivalent in modern SDK.
    - **Resolution**: Read credential file with Jackson ObjectMapper, construct ClientSecretCredential, use AzureProfile.
    - **Files Changed**: AzureHelper.java, AppConfig.java

  - **Fluent API Changes**
    - **Issue**: Method names differ between legacy Azure.management and modern azure-resourcemanager.
    - **Resolution**: Followed migration guide at https://aka.ms/java-track2-migration-guide
    - **Files Changed**: ResourceProvisioner.java, StorageSetup.java
-->

## Limitations

<!--
  Document any genuinely unfixable limitations that remain after the upgrade.
  This section should be empty if all issues were resolved.
  Only include items where: (1) multiple fix approaches were attempted, (2) root cause is identified,
  (3) fix is technically impossible without breaking other functionality.
-->

## Review Code Changes Summary

<!--
  Document review code changes results from the upgrade.
  This section ensures the upgrade is both sufficient (complete) and necessary (no extraneous changes),
  with original functionality and security controls preserved.

  VERIFICATION AREAS:
  1. Sufficiency: All required upgrade changes are present β€” no missing modifications
  2. Necessity: All changes are strictly necessary β€” no unnecessary modifications, including:
     - Functional Behavior Consistency: Business logic, API contracts, expected outputs
     - Security Controls Preservation (critical subset of behavior):
       - Authentication: Login mechanisms, session management, token validation, MFA configurations
       - Authorization: Role-based access control, permission checks, access policies, security annotations (@PreAuthorize, @Secured, etc.)
       - Password handling: Password encoding/hashing algorithms, password policies, credential storage
       - Security configurations: CORS policies, CSRF protection, security headers, SSL/TLS settings, OAuth/OIDC configurations
       - Audit logging: Security event logging, access logging

  SAMPLE (no issues):
  **Review Status**: βœ… All Passed

  **Sufficiency**: βœ… All required upgrade changes are present
  **Necessity**: βœ… All changes are strictly necessary
  - Functional Behavior: βœ… Preserved β€” business logic, API contracts unchanged
  - Security Controls: βœ… Preserved β€” authentication, authorization, password handling, security configs, audit logging unchanged

  SAMPLE (with behavior changes):
  **Review Status**: ⚠️ Changes Documented Below

  **Sufficiency**: βœ… All required upgrade changes are present

  **Necessity**: ⚠️ Behavior changes required by SDK migration (documented below)
  - Functional Behavior: βœ… Preserved
  - Security Controls: ⚠️ Changes made with equivalent protection

  | Area               | Change Made                                      | Reason                                         | Equivalent Behavior   |
  | ------------------ | ------------------------------------------------ | ---------------------------------------------- | --------------------- |
  | Authentication     | Azure.authenticate() β†’ ClientSecretCredential    | Legacy auth API removed in modern SDK           | βœ… Same credentials   |
-->

## Next Steps

<!--
  Recommendations for post-upgrade actions.

  SAMPLE:
  - [ ] Run full integration test suite in staging environment
  - [ ] Performance testing to validate no regression
  - [ ] Update CI/CD pipelines if dependency versions changed
  - [ ] Update documentation to reflect new Azure SDK versions
  - [ ] Review azure-sdk-bom version periodically for updates
-->

## Artifacts

- **Plan**: `.github/java-upgrade/<RUN_ID>/plan.md`
- **Progress**: `.github/java-upgrade/<RUN_ID>/progress.md`
- **Summary**: `.github/java-upgrade/<RUN_ID>/summary.md` (this file)
- **Branch**: `java-upgrade/<RUN_ID>`
references/languages/java/workflow/
phase-1-precheck.md 2.3 KB
# Phase 1: Precheck

Load this file when executing Phase 1. Refer back to [`upgrade-success-criteria`](../rules/upgrade-success-criteria.md) and [`upgrade-strategy`](../rules/upgrade-strategy.md) for success criteria and strategy, and [`../rules/troubleshooting.md`](../rules/troubleshooting.md) when failures occur.

| Category            | Scenario                         | Action                                                 |
| ------------------- | -------------------------------- | ------------------------------------------------------ |
| Unsupported Project | Not a Maven/Gradle project       | STOP with error                                        |
| Unsupported Project | Git not installed or not managed | Log warning, continue without git                      |
| Invalid Goal        | No legacy Azure SDK deps found   | STOP β€” nothing to migrate                              |
| Java Version        | Below JDK 8                      | Include Java upgrade as part of the migration plan     |

**Prerequisites**: JDK 8+ and Maven or Gradle must be pre-installed.

**Environment detection**:

Detect available JDKs:
1. Check `JAVA_HOME` and `JDK_HOME` environment variables
2. Run `java -version` and `javac -version` to detect the default JDK
3. Search common JDK installation paths (platform-specific: Program Files on Windows, /usr/lib/jvm on Linux, /Library/Java on macOS)
4. Check for version manager installations (SDKMAN, ASDF, jenv, Jabba)
5. For each found JDK, read the `release` file to determine the version

Report all found JDKs with their path, version, and discovery source.

Detect build tools:
1. Check for Maven Wrapper (`mvnw`/`mvnw.cmd`) or Gradle Wrapper (`gradlew`/`gradlew.bat`) in the project root β€” prefer wrappers when present
2. If a wrapper exists, read `.mvn/wrapper/maven-wrapper.properties` or `gradle/wrapper/gradle-wrapper.properties` to determine the wrapper-defined version
3. Run `mvn --version` or `gradle --version` to detect system installations
4. Check `MAVEN_HOME`/`M2_HOME` environment variables

Report all found installations with their path, version, and source.

**On success**: Create `.github/java-upgrade/{RUN_ID}/plan.md` from the Plan Template β€” replace placeholders (`<RUN_ID>`, `<PROJECT_NAME>`, `<current_branch>`, `<current_commit_id>`, datetime) and follow the HTML-comment instructions to populate each section.
phase-2-plan.md 2.9 KB
# Phase 2: Generate Upgrade Plan

Load this file when executing Phase 2. Refer back to [`upgrade-success-criteria`](../rules/upgrade-success-criteria.md) and [`upgrade-strategy`](../rules/upgrade-strategy.md) for success criteria and strategy, and [`../rules/troubleshooting.md`](../rules/troubleshooting.md) when failures occur.

## 1. Initialize

1. Update `plan.md`: replace all remaining placeholders
2. Extract user-specified guidelines from prompt into "Guidelines" section (bulleted list; leave empty if none)

## 2. Environment Analysis

1. Read HTML comments in "Available Tools" section of `plan.md` to understand rules and expected format
2. Record discovered JDK versions and paths
3. Detect wrapper presence; if wrapper exists, read wrapper properties to determine build tool version
4. Check build tool version compatibility with JDK β€” flag incompatible versions for upgrade

## 3. Dependency Analysis

1. Read HTML comments in "Technology Stack" and "Derived Upgrades" sections of `plan.md` to understand rules and expected format
2. Identify core tech stack across **ALL modules** (direct deps + upgrade-critical deps)
3. Include build tool (Maven/Gradle) and build plugins (`maven-compiler-plugin`, `maven-surefire-plugin`, etc.) in the technology stack analysis
4. Flag EOL dependencies (high priority for upgrade)
5. Consult the Migration Guidelines for package mappings and migration guides
6. Populate "Technology Stack" and "Derived Upgrades"

## 4. Upgrade Path Design

1. Read HTML comments in "Key Challenges" and "Upgrade Steps" sections of `plan.md` to understand rules and expected format
2. For incompatible deps, prefer: Replacement > Adaptation > Rewrite
3. Finalize "Available Tools" section based on the planned step sequence
4. Design step sequence:
   - **Step 1 (MANDATORY)**: Setup Baseline β€” run compile/test with current JDK, document results
   - **Steps 2-N**: Upgrade steps β€” dependency order, high-risk early, isolated breaking changes. Compilation must pass (both main and test code); test failures documented for Final Validation.
   - **Final step (MANDATORY)**: Final Validation β€” verify all goals met, all TODOs resolved, achieve **Upgrade Success Criteria** through iterative test & fix loop.
5. Identify high-risk areas for "Key Challenges" section
6. Write steps following format in `plan.md`

## 5. Plan Review

1. Verify all placeholders filled in `plan.md`, check for missing coverage/infeasibility/limitations
2. Revise plan as needed for completeness and feasibility; document unfixable limitations in "Plan Review" section
3. Ensure all sections of `plan.md` are fully populated (per **Template compliance** rule) and all HTML comments removed

After plan generation, proceed directly to execution β€” create `.github/java-upgrade/{RUN_ID}/progress.md` from the Progress Template, replace placeholders, and begin execution. Log the migration plan, then proceed without pausing for confirmation.
phase-3-execute.md 2.4 KB
# Phase 3: Execute Upgrade Plan

Load this file when executing Phase 3. Refer back to [`upgrade-success-criteria`](../rules/upgrade-success-criteria.md) and [`upgrade-strategy`](../rules/upgrade-strategy.md) for success criteria and strategy, and [`../rules/troubleshooting.md`](../rules/troubleshooting.md) when failures occur.

## 1. Initialize

1. Read `plan.md` for step details
2. Update `progress.md`:
   - Replace `<RUN_ID>`, `<PROJECT_NAME>` and timestamp placeholders
   - Create step entries for each step in `plan.md` (per **Template compliance** rule)

## 2. Execute

For each step:

1. Read `plan.md` for step details and guidelines
2. Mark ⏳ in `progress.md`
3. Make changes as planned (use OpenRewrite if helpful, verify results)
   - Add TODOs for any deferred work, e.g., temporary workarounds
4. **Review Code Changes** (per rules in Progress Template): Verify sufficiency (all required changes present) and necessity (no unnecessary changes, functional behavior preserved, security controls maintained).
   - Add missing changes and revert unnecessary changes. Document any unavoidable behavior changes with justification.
5. Verify with specified command/JDK:
   - **Steps 1-N (Setup/Upgrade)**: Compilation must pass (including both main and test code, fix immediately if not). Test failures acceptable β€” document count.
   - **Final Validation Step**: Achieve **Upgrade Success Criteria** β€” iterative test & fix loop until 100% pass (or β‰₯ baseline). NO deferring. See [`../rules/troubleshooting.md`](../rules/troubleshooting.md) when stuck.
   - Build: `mvn clean test-compile` (or `./gradlew compileTestJava` for Gradle)
   - Test: `mvn clean test` (or `./gradlew test` for Gradle)
6. Commit on the `java-upgrade/{RUN_ID}` branch with message format (if git available; otherwise, log details in `progress.md`):
   - First line: `Step <x>: <title> - Compile: <result>` or `Step <x>: <title> - Compile: <result>, Tests: <pass>/<total> passed` (if tests run)
   - Body: Changes summary + concise known issues/limitations (≀5 lines)
   - **Security note**: If any security-related changes were made, include "Security: <change description and justification>"
7. Update `progress.md` with step details and mark βœ… or ❗

## 3. Complete

1. Validate all steps in `plan.md` have βœ… in `progress.md`
2. Validate all **Upgrade Success Criteria** are met, or otherwise go back to Final Validation step to fix
phase-4-summarize.md 1.1 KB
# Phase 4: Summarize & Validate

Load this file when executing Phase 4. Refer back to [`upgrade-success-criteria`](../rules/upgrade-success-criteria.md) and [`upgrade-strategy`](../rules/upgrade-strategy.md) for success criteria and strategy, and [`../rules/troubleshooting.md`](../rules/troubleshooting.md) when failures occur.

1. Create `.github/java-upgrade/{RUN_ID}/summary.md` from the Summary Template β€” replace placeholders and follow HTML-comment instructions to populate final results.
2. Apply the validation checklist from the Migration Guidelines:
   - Migrated project passes compilation
   - All tests pass β€” don't silently skip tests
   - No legacy SDK dependencies/references exist
   - If `azure-sdk-bom` is used, ensure no explicit version dependencies for Azure libraries in the BOM
   - For each migration guide recorded during migration, fetch and verify the migrated code follows the guide's recommendations. Fix any deviations.
3. Populate `summary.md` (Upgrade Result, Tech Stack Changes, Commits, Challenges, Limitations, Next Steps)
4. Clean up temp files; remove HTML comments from all `.md` files
5. Verify all goals met
references/services/functions/
assessment.md 4.1 KB
# Assessment: Functions Plan Upgrade

Generate an upgrade assessment report before any changes to Azure resources.

## Prerequisites

- User has an existing Azure Functions app on a Consumption or other plan
- User has Azure CLI v2.77.0+ installed
- User has Owner or Contributor role in the target resource group
- The `resource-graph` extension is installed (`az extension add --name resource-graph`)

## Assessment Steps

1. **Identify Source App** β€” Confirm the function app name, resource group, region, and current hosting plan
2. **Check Region Compatibility** β€” Verify the target plan is available in the app's region
3. **Verify Language Stack** β€” Confirm the app's runtime is supported on the target plan
4. **Verify Stack Version** β€” Confirm the runtime version is supported on the target plan in the region
5. **Check Deployment Slots** β€” Determine if slots are in use (Flex Consumption doesn't support slots)
6. **Check Certificates** β€” Determine if TLS/SSL certificates are in use (not yet supported in Flex Consumption)
7. **Check Blob Triggers** β€” Verify blob triggers use EventGrid source (container polling not supported in Flex Consumption)
8. **Assess Dependencies** β€” Review upstream and downstream service dependencies and plan mitigation strategies
9. **Generate Report** β€” Create `upgrade-assessment-report.md`

## Assessment Report Format

> ⚠️ **MANDATORY**: Use these exact section headings in every assessment report. Do NOT rename, reorder, or omit sections.

The report MUST be saved as `upgrade-assessment-report.md` in the workspace root.

```markdown
# Upgrade Assessment Report

## 1. Executive Summary

| Property | Value |
|----------|-------|
| **App Name** | <app-name> |
| **Resource Group** | <resource-group> |
| **Current Plan** | <current-plan (e.g., Consumption / Y1 Dynamic)> |
| **Target Plan** | <target-plan (e.g., Flex Consumption / FC1)> |
| **Region** | <region> |
| **Runtime** | <runtime and version> |
| **OS** | <Linux / Windows> |
| **Upgrade Readiness** | <Ready / Needs Attention / Blocked> |
| **Assessment Date** | <date> |

## 2. Compatibility Checks

| Check | Status | Details |
|-------|--------|---------|
| Region supported | βœ… / ❌ | |
| Language stack supported | βœ… / ❌ | |
| Stack version supported | βœ… / ❌ | |
| No deployment slots | βœ… / ⚠️ | |
| No TLS/SSL certificates | βœ… / ⚠️ | |
| Blob triggers use EventGrid | βœ… / ⚠️ / N/A | |
| .NET isolated (not in-process) | βœ… / ❌ / N/A | |

## 3. App Settings Inventory

| Setting | Value | Migrate? | Notes |
|---------|-------|----------|-------|
| | | Yes / No / Convert | |

## 4. Managed Identities

| Type | Principal ID | Roles | Action |
|------|-------------|-------|--------|
| System-assigned | | | Recreate in new app |
| User-assigned | | | Reassign to new app |

## 5. Application Configurations

| Configuration | Current Value | Migrate? | Notes |
|---------------|---------------|----------|-------|
| CORS settings | | | |
| Custom domains | | | |
| HTTP version | | | |
| HTTPS only | | | |
| TLS version | | | |
| Client certificates | | | |
| Access restrictions | | | |
| Built-in auth | | | |

## 6. Trigger & Binding Analysis

| Function | Trigger Type | Source | Migration Risk | Mitigation |
|----------|-------------|--------|----------------|------------|
| | | | Low / Medium / High | |

## 7. Dependent Services

| Service | Dependency Type | Migration Risk | Mitigation Strategy |
|---------|----------------|----------------|---------------------|
| | Upstream / Downstream | | |

## 8. Blockers & Warnings

### Blockers (must fix before upgrade)
- [ ] <any blocking issues>

### Warnings (should address but not blocking)
- [ ] <any non-blocking concerns>

## 9. Recommendations

1. **Plan**: <recommended target plan>
2. **Auth**: <switch to Managed Identity if using connection strings>
3. **Monitoring**: <Application Insights configuration>
4. **Scaling**: <recommended instance count and concurrency settings>

## 10. Next Steps

- [ ] Review and approve this assessment
- [ ] Address any blockers listed above
- [ ] Proceed to automated upgrade (Phase 3-4)
```

> πŸ’‘ **Tip:** Use `mcp_azure_mcp_get_azure_bestpractices` to get the latest recommendations for the target hosting plan.
automation.md 13.5 KB
# Automation: Consumption to Flex Consumption Upgrade

> These are the Azure CLI scripts to automate the upgrade from Linux Consumption plan to Flex Consumption plan.
> All scripts use `bash` syntax compatible with Azure Cloud Shell. For PowerShell, adapt accordingly.
>
> **Source docs**: [Linux migration guide](https://learn.microsoft.com/en-us/azure/azure-functions/migration/migrate-plan-consumption-to-flex?pivots=platform-linux)

## Prerequisites

```bash
# Ensure Azure CLI v2.77.0+
az --version

# Install resource-graph extension
az extension add --name resource-graph

# Login and set subscription
az login
az account set --subscription <SUBSCRIPTION_ID>
```

---

## Step 1: Identify Candidate Apps

```bash
# List all Linux Consumption apps with eligibility status
az functionapp flex-migration list
```

This returns two arrays:
- `eligible_apps` β€” apps that can be migrated to Flex Consumption
- `ineligible_apps` β€” apps with specific reasons why not

The output includes app name, resource group, location, and runtime stack for each app.

---

## Step 2: Assessment Checks

Set variables for your app:

```bash
appName=<APP_NAME>
rgName=<RESOURCE_GROUP>
```

### 2a. Confirm Region Compatibility

```bash
# List all regions where Flex Consumption is available
az functionapp list-flexconsumption-locations --query "sort_by(@, &name)[].{Region:name}" -o table
```

### 2b. Verify Language Stack Compatibility

Supported stacks: `dotnet-isolated`, `node`, `java`, `python`, `powershell`, `custom`.
**Not supported**: `dotnet` (in-process) β€” must migrate to isolated first.

### 2c. Verify Stack Version Compatibility

```bash
# Check supported versions for a specific runtime in a specific region
az functionapp list-flexconsumption-runtimes --location <REGION> --runtime <LANGUAGE_STACK> \
    --query '[].{version:version}' -o tsv
```

Replace `<REGION>` with the app's region and `<LANGUAGE_STACK>` with one of: `dotnet-isolated`, `java`, `node`, `powershell`, `python`.

### 2d. Check Deployment Slots

```bash
# List any deployment slots
az functionapp deployment slot list --name $appName --resource-group $rgName --output table
```

If this returns entries, the app has slots. Flex Consumption does NOT support slots β€” plan accordingly.

### 2e. Check TLS/SSL Certificates

```bash
# List certificates available to the app
az webapp config ssl list --resource-group $rgName
```

If this returns output, the app likely uses certificates. Flex Consumption does NOT support certs yet.

### 2f. Check Blob Storage Triggers

```bash
# Find blob triggers NOT using EventGrid source
az functionapp function list --name $appName --resource-group $rgName \
  --query "[?config.bindings[0].type=='blobTrigger' && config.bindings[0].source!='EventGrid'].{Function:name,TriggerType:config.bindings[0].type,Source:config.bindings[0].source}" \
  --output table
```

If this returns rows, convert those blob triggers from `LogsAndContainerScan` to `EventGrid` before upgrading.

---

## Step 3: Pre-Migration β€” Collect Settings

### 3a. Collect App Settings

```bash
appName=<APP_NAME>
rgName=<RESOURCE_GROUP>

# Get all app settings as JSON
app_settings=$(az functionapp config appsettings list --name $appName --resource-group $rgName)
echo "$app_settings"
```

### 3b. Collect Application Configurations

```bash
appName=<APP_NAME>
rgName=<RESOURCE_GROUP>

echo "Getting commonly used site settings..."
az functionapp config show --name $appName --resource-group $rgName \
    --query "{http20Enabled:http20Enabled, httpsOnly:httpsOnly, minTlsVersion:minTlsVersion, \
    minTlsCipherSuite:minTlsCipherSuite, clientCertEnabled:clientCertEnabled, \
    clientCertMode:clientCertMode, clientCertExclusionPaths:clientCertExclusionPaths}"

echo "Checking for SCM basic publishing credentials policies..."
az resource show --resource-group $rgName --name scm --namespace Microsoft.Web \
    --resource-type basicPublishingCredentialsPolicies --parent sites/$appName --query properties

echo "Checking for the maximum scale-out limit configuration..."
az functionapp config appsettings list --name $appName --resource-group $rgName \
    --query "[?name=='WEBSITE_MAX_DYNAMIC_APPLICATION_SCALE_OUT'].value" -o tsv

echo "Checking for any file share mount configurations..."
az webapp config storage-account list --name $appName --resource-group $rgName

echo "Checking for any custom domains..."
az functionapp config hostname list --webapp-name $appName --resource-group $rgName \
    --query "[?contains(name, 'azurewebsites.net')==\`false\`]" --output table

echo "Checking for any CORS settings..."
az functionapp cors show --name $appName --resource-group $rgName
```

### 3c. Identify Managed Identities and Role Assignments

```bash
appName=<APP_NAME>
rgName=<RESOURCE_GROUP>

echo "Checking for a system-assigned managed identity..."
systemUserId=$(az functionapp identity show --name $appName --resource-group $rgName \
    --query "principalId" -o tsv)

if [[ -n "$systemUserId" ]]; then
    echo "System-assigned identity principal ID: $systemUserId"
    echo "Checking for role assignments..."
    az role assignment list --assignee $systemUserId --all
else
    echo "No system-assigned identity found."
fi

echo "Checking for user-assigned managed identities..."
userIdentities=$(az functionapp identity show --name $appName --resource-group $rgName \
    --query 'userAssignedIdentities' -o json)

if [[ "$userIdentities" != "{}" && "$userIdentities" != "null" ]]; then
    echo "$userIdentities" | jq -c 'to_entries[]' | while read -r identity; do
        echo "User-assigned identity: $(echo "$identity" | jq -r '.key' | sed 's|.*/userAssignedIdentities/||')"
        echo "Checking for role assignments..."
        az role assignment list --assignee $(echo "$identity" | jq -r '.value.principalId') --all --output json
        echo
    done
else
    echo "No user-assigned identities found."
fi
```

### 3d. Check Built-in Authentication

```bash
az webapp auth show --name $appName --resource-group $rgName
```

### 3e. Review Inbound Access Restrictions

```bash
az functionapp config access-restriction show --name $appName --resource-group $rgName
```

### 3f. Get Deployment Package (if needed)

Ideally your project files are in source control and you can redeploy from there. If not:

#### Check WEBSITE_RUN_FROM_PACKAGE

```bash
az functionapp config appsettings list --name $appName --resource-group $rgName \
    --query "[?name=='WEBSITE_RUN_FROM_PACKAGE'].value" -o tsv
```

If this returns a URL, download the package from that remote location.

#### Download from scm-releases blob container

Linux Consumption apps store deployment packages in the `scm-releases` blob container (in `squashfs` format).

```bash
appName=<APP_NAME>
rgName=<RESOURCE_GROUP>

echo "Getting the storage account connection string..."
storageConnection=$(az functionapp config appsettings list --name $appName --resource-group $rgName \
    --query "[?name=='AzureWebJobsStorage'].value" -o tsv)

echo "Getting the package name..."
packageName=$(az storage blob list --connection-string $storageConnection --container-name scm-releases \
    --query "[0].name" -o tsv)

echo "Downloading package: $packageName"
az storage blob download --connection-string $storageConnection --container-name scm-releases \
    --name $packageName --file $packageName
```

> πŸ’‘ If your storage account is restricted to managed identity access only, you may need to grant your Azure account the `Storage Blob Data Reader` role.

---

## Step 4: Create the Flex Consumption App

```bash
# Automated migration β€” creates new app and migrates most configurations
az functionapp flex-migration start \
    --source-name <SOURCE_APP_NAME> \
    --source-resource-group <SOURCE_RESOURCE_GROUP> \
    --name <NEW_APP_NAME> \
    --resource-group <RESOURCE_GROUP>
```

**Optional flags**:
- `--storage-account <ACCOUNT>` β€” use a different storage account
- `--maximum-instance-count <COUNT>` β€” set max scale-out instances
- `--skip-access-restrictions` β€” skip migrating IP access restrictions
- `--skip-cors` β€” skip migrating CORS settings
- `--skip-hostnames` β€” skip migrating custom domains
- `--skip-managed-identities` β€” skip migrating managed identity configurations
- `--skip-storage-mount` β€” skip migrating storage mount configurations

The command automatically:
- Assesses your source app for Flex Consumption compatibility
- Creates a new function app in the Flex Consumption plan
- Migrates app settings, identity assignments, storage mounts, CORS, custom domains, and access restrictions

### Verify Migration Results

```bash
# Verify new app exists and is configured
az functionapp show --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \
    --query "{name:name, kind:kind, sku:properties.sku}" --output table

# Review migrated app settings
az functionapp config appsettings list --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \
    --output table

# Check managed identity
az functionapp identity show --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP>

# Check custom domains
az functionapp config hostname list --webapp-name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \
    --output table
```

### Configure Items Not Auto-Migrated

The `flex-migration start` command handles most settings, but these may need manual configuration:

#### Built-in Authentication

```bash
# Recreate auth settings if your original app used Easy Auth
az webapp auth update --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \
    --enabled true --action <AUTH_ACTION>
```

#### Scale and Concurrency (if custom values needed)

```bash
# Set maximum scale-out (default: 100, range: 1-1000)
az functionapp scale config set --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \
    --maximum-instance-count <MAX_SCALE_SETTING>
```

> ⚠️ Reducing below 40 for HTTP apps can cause frequent request failures.

---

## Step 5: Deploy Code

> ⚠️ **Code is NOT automatically migrated.** The new app is created with config only β€” you must deploy code separately.

### ask_user: Choose Deployment Method

Present these options to the user:

> Your new Flex Consumption app `<NEW_APP_NAME>` has been created and configured. Now we need to deploy your function code. How would you like to proceed?
>
> 1. **Update CI/CD pipeline** β€” I'll help you update your Azure Pipelines or GitHub Actions workflow to target the new app
> 2. **Deploy from local project** β€” I'll run `func azure functionapp publish <NEW_APP_NAME>` from your project directory  
> 3. **Deploy existing package** β€” I'll deploy the package we downloaded earlier from the original app

---

### Option A: Update CI/CD Pipeline (if user selects option 1)

Update your existing pipeline (Azure Pipelines or GitHub Actions) to target the new app name.

- [Build and deploy with Azure Pipelines](https://learn.microsoft.com/en-us/azure/azure-functions/functions-how-to-azure-devops)
- [Build and deploy with GitHub Actions](https://learn.microsoft.com/en-us/azure/azure-functions/functions-how-to-github-actions)

### Option B: Deploy from Local Project (if user selects option 2)

```bash
# From your project directory
func azure functionapp publish <NEW_APP_NAME>
```

### Option C: Deploy Existing Package (if user selects option 3)

```bash
# Deploy the zip package downloaded in Step 3
az functionapp deployment source config-zip --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \
    --src <PACKAGE_PATH.zip>
```

### After Successful Deployment

Inform the user:

> Code deployed! Next steps to consider:
>
> - The original app is still running β€” keep it as rollback for a few days
> - Update any clients/pipelines to point to the new URL
> - Enable HTTPS-only and managed identity on the new app for better security
> - When confident, you can delete the original app

---

## Step 6: Post-Upgrade Validation

### Smoke Test (run this first)

```bash
# Minimum viability check β€” confirm the app is reachable at all
DEFAULT_HOST=$(az functionapp show --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> \
    --query "defaultHostName" -o tsv)

HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://$DEFAULT_HOST")
echo "App responded with HTTP $HTTP_STATUS"

# Expected: 2xx or 401/404 (means the host is up). 
# If 503 or connection refused β†’ the app failed to start. Check logs:
#   az functionapp log tail --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP>
```

### Verify Plan

```bash
az functionapp show --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP> --query "serverFarmId"
```

### Test HTTP Endpoints

```bash
# Test an HTTP trigger function
curl -s -o /dev/null -w "%{http_code}" "https://$DEFAULT_HOST/api/<FUNCTION_NAME>"
```

### Performance Benchmarks (Application Insights KQL)

```kql
requests
| where timestamp > ago(1d)
| summarize percentiles(duration, 50, 95, 99) by bin(timestamp, 1h)
| render timechart
```

### Check for Errors

```kql
traces
| where severityLevel == 3
| where cloud_RoleName == "<NEW_APP_NAME>"
| where timestamp > ago(1d)
| project timestamp, message, operation_Name, customDimensions
| order by timestamp desc
```

---

## Step 7: Cleanup (Optional)

```bash
# β›” REQUIRES ask_user confirmation before executing

# Delete the original function app
az functionapp delete --name <ORIGINAL_APP_NAME> --resource-group <RESOURCE_GROUP>
```

> πŸ’‘ No rush. The Consumption plan only charges for actual usage, so keeping the old app (with triggers disabled) costs very little. We recommend keeping it for a few days/weeks.

---

## Rollback

```bash
# Restart the original app if it was stopped
az functionapp start --name <ORIGINAL_APP_NAME> --resource-group <RESOURCE_GROUP>

# Optionally delete the new Flex Consumption app
az functionapp delete --name <NEW_APP_NAME> --resource-group <RESOURCE_GROUP>
```
consumption-to-flex.md 10.8 KB
# Consumption Plan to Flex Consumption Plan Upgrade

> **Source**: Azure Functions Consumption Plan (Y1/Dynamic) on Linux
> **Target**: Azure Functions Flex Consumption Plan (FC1/FlexConsumption)
> **Platform**: Linux only (Windows support planned for future)
> **Docs**: [Linux migration guide](https://learn.microsoft.com/en-us/azure/azure-functions/migration/migrate-plan-consumption-to-flex?pivots=platform-linux)

## Why Upgrade?

- **Faster cold starts** β€” always-ready instances mean functions respond more quickly
- **Better scaling** β€” per-function scaling and concurrency controls
- **Virtual network support** β€” connect to private networks and use private endpoints
- **Active investment** β€” Flex Consumption is where new features land first

## What to Expect

1. Your **code stays the same** β€” no rewriting required if on a supported language version
2. You'll **create a new app** alongside the existing one
3. New app runs in the **same resource group** with access to the same dependencies
4. You **control the timing** β€” test thoroughly before switching over

## Platform Notes

- Linux Consumption is being **retired September 30, 2028**
- Azure CLI provides automated migration commands: `az functionapp flex-migration list` and `az functionapp flex-migration start`
- The `flex-migration` commands handle assessment, app creation, and most configuration migration automatically
- Deployment packages are in `squashfs` format stored in `scm-releases` blob container

## Prerequisites

- Azure CLI v2.77.0+
- `resource-graph` extension: `az extension add --name resource-graph`
- `jq` tool for JSON processing
- Owner or Contributor role in the resource group
- Permissions to create and manage function apps, storage accounts, App Insights resources, and managed identity role assignments

## Compatibility Requirements

### Supported Language Stacks

| Stack ID | Language | Supported? |
|----------|----------|------------|
| `dotnet-isolated` | .NET (isolated worker model) | βœ… Yes |
| `node` | JavaScript/TypeScript | βœ… Yes |
| `java` | Java | βœ… Yes |
| `python` | Python | βœ… Yes |
| `powershell` | PowerShell | βœ… Yes |
| `dotnet` | .NET (in-process model) | ❌ No β€” must migrate to isolated first |
| `custom` | Custom handlers | βœ… Yes |

### Known Limitations (Flex Consumption)

| Feature | Status | Impact |
|---------|--------|--------|
| Deployment slots | ❌ Not supported | Rearchitect to use separate apps |
| TLS/SSL certificates | ❌ Not supported | Wait for support or find alternative |
| Blob trigger (polling) | ❌ Only EventGrid source | Convert `LogsAndContainerScan` β†’ `EventGrid` |
| Azure Government | ❌ Not available | Cannot migrate yet |

## Upgrade Phases

### Phase 1: Assessment

Run all checks from [assessment.md](assessment.md). Use the automated eligibility check:

```bash
# Automated eligibility check β€” scans all Linux Consumption apps
az functionapp flex-migration list
```

This returns `eligible_apps` and `ineligible_apps` arrays with specific reasons for any incompatibilities. For detailed manual checks, see [automation.md](automation.md).

### Phase 2: Pre-migration (Collect Everything)

Collect all settings and configurations from the existing app before creating the new one. See **Step-by-step scripts** in [automation.md](automation.md):

1. Collect app settings
2. Collect application configurations (CORS, custom domains, HTTP version, TLS, client certs, etc.)
3. Identify managed identities and RBAC role assignments
4. Identify built-in authentication settings
5. Review inbound access restrictions
6. Get the code deployment package (if source code not in version control)

### Phase 3: Create Flex Consumption App

Run the automated migration command from [automation.md](automation.md) β€” Step 4.

The `flex-migration start` command automatically:
- Assesses your source app for Flex Consumption compatibility
- Creates a new function app in the Flex Consumption plan
- Migrates app settings, identity assignments, storage mounts, CORS, custom domains, and access restrictions

> πŸ’‘ Use Microsoft Entra ID + managed identities instead of connection strings when creating the new app.

### Identity-First Configuration (Functions)

Enterprise subscriptions commonly enforce policies blocking local auth. Configure identity-based access:

- **Storage accounts**: Use `AzureWebJobsStorage__credential`, `__clientId`, and service-specific URIs (`__blobServiceUri`, `__queueServiceUri`, `__tableServiceUri`)
- **Application Insights**: Use `APPLICATIONINSIGHTS_AUTHENTICATION_STRING` with `Authorization=AAD`
- When using User Assigned Managed Identity, pass `managedIdentityClientId` explicitly

### Phase 4: Verify and Configure the New App

The `flex-migration start` command handles most configuration. Verify the results and manually configure anything it doesn't cover (see [automation.md](automation.md)):

1. **Verify** migrated app settings, identities, custom domains
2. **Configure** built-in authentication (if used β€” not auto-migrated)
3. **Configure** scale and concurrency settings (if custom values needed)
4. **Enable** Application Insights monitoring

### Phase 5: Deploy Code

> ⚠️ **Code is NOT automatically migrated.** The new app is created with config only β€” you must deploy code separately.

**Use `ask_user` to present these options:**

> Your new Flex Consumption app `<NEW_APP_NAME>` has been created and configured. Now we need to deploy your function code. How would you like to proceed?
>
> 1. **Update CI/CD pipeline** β€” I'll help you update your Azure Pipelines or GitHub Actions workflow to target the new app
> 2. **Deploy from local project** β€” I'll run `func azure functionapp publish <NEW_APP_NAME>` from your project directory
> 3. **Deploy existing package** β€” I'll deploy the package we downloaded earlier from the original app

After user selects an option, execute the corresponding deployment method from [automation.md](automation.md) β€” Step 5.

> ⚠️ After deployment, triggers immediately start processing. Review mitigation strategies for your trigger types.

**After successful deployment, inform the user:**

> Code deployed! Next steps to consider:
>
> - The original app is still running β€” keep it as rollback for a few days
> - Update any clients/pipelines to point to the new URL
> - Enable HTTPS-only and managed identity on the new app for better security
> - When confident, you can delete the original app

### Phase 6: Post-Upgrade Validation

1. **Smoke test** β€” Get the app’s default hostname via `az functionapp show --query defaultHostName -o tsv`, then hit that URL and confirm it returns a non-error response (HTTP 2xx/4xx, not connection refused or 503). This is the minimum bar before proceeding.
2. Verify the app is running on Flex Consumption (`az functionapp show --query sku`)
3. Test HTTP trigger endpoints (e.g. `curl https://<DEFAULT_HOST>/api/<FUNCTION_NAME>`)
4. Capture and compare performance benchmarks
5. Set up monitoring dashboards
6. Refine scale/concurrency settings
7. Update IaC (Bicep/Terraform) to reflect new plan

### Phase 7: Cleanup (Optional)

- Keep the original app for a few days/weeks as rollback
- Consumption plan charges only for actual usage β€” low cost to keep idle
- When confident, delete using the command in [automation.md](automation.md) β€” Step 7

## Trigger Migration Risks

| Trigger Type | Risk | Mitigation |
|-------------|------|------------|
| Azure Blob storage | High | Create separate container for event-based trigger in new app |
| Azure Cosmos DB | High | Create dedicated lease container for new app; set `StartFromBeginning: false` |
| Azure Event Grid | Medium | Recreate event subscriptions; ensure idempotent functions |
| Azure Event Hubs | Medium | Create new consumer group for new app |
| Azure Service Bus | High | Create new topic/queue; update senders; drain original before shutdown |
| Azure Storage Queue | High | Create new queue; update senders; drain original before shutdown |
| HTTP | Low | Update clients to target new app URL |
| Timer | Low | Offset schedules during cutover to avoid simultaneous execution |

## IaC Key Differences

| Property | Consumption | Flex Consumption |
|----------|-------------|------------------|
| SKU | `Y1` (Dynamic) | `FC1` (FlexConsumption) |
| Plan required | Optional (auto-created) | Required (must be explicit) |
| OS | Linux | Linux only |
| Configuration | App settings | `functionAppConfig` section |
| Storage | `WEBSITE_CONTENTSHARE` setting | `deployment.storage` in `functionAppConfig` |

## Deprecated Settings (Do NOT Migrate)

These app settings are NOT supported in Flex Consumption and should be filtered out:

- `WEBSITE_USE_PLACEHOLDER_DOTNETISOLATED`
- `AzureWebJobsStorage*` (replaced by identity-based config)
- `WEBSITE_MOUNT_ENABLED`
- `ENABLE_ORYX_BUILD`
- `FUNCTIONS_EXTENSION_VERSION` (set via `functionAppConfig`)
- `FUNCTIONS_WORKER_RUNTIME` (set via `functionAppConfig`)
- `FUNCTIONS_WORKER_RUNTIME_VERSION`
- `FUNCTIONS_MAX_HTTP_CONCURRENCY`
- `FUNCTIONS_WORKER_PROCESS_COUNT`
- `FUNCTIONS_WORKER_DYNAMIC_CONCURRENCY_ENABLED`
- `SCM_DO_BUILD_DURING_DEPLOYMENT`
- `WEBSITE_CONTENTAZUREFILECONNECTIONSTRING`
- `WEBSITE_CONTENTOVERVNET`
- `WEBSITE_CONTENTSHARE`
- `WEBSITE_DNS_SERVER`
- `WEBSITE_MAX_DYNAMIC_APPLICATION_SCALE_OUT`
- `WEBSITE_NODE_DEFAULT_VERSION`
- `WEBSITE_RUN_FROM_PACKAGE`
- `WEBSITE_SKIP_CONTENTSHARE_VALIDATION`
- `WEBSITE_VNET_ROUTE_ALL`
- `APPLICATIONINSIGHTS_CONNECTION_STRING` (already created in new app)

## Troubleshooting

| Issue | Remediation |
|-------|-------------|
| Cold start performance issues | Review concurrency settings; check for missing dependencies |
| Missing bindings | Verify extension bundles; update binding configurations |
| Permission errors | Check identity assignments and role permissions |
| Network connectivity | Validate access restrictions and networking settings |
| Missing App Insights | Recreate the Application Insights connection |
| App fails to start | Check portal Diagnose & Solve; review App Insights Failures blade |
| Triggers not processing | Verify binding configs, connection strings, consumer groups |

## Rollback

1. Restart the original app: `az functionapp start --name <ORIGINAL_APP_NAME> --resource-group <RESOURCE_GROUP>`
2. Redirect clients back to original resources (queues/topics/containers)
3. Revert DNS or custom domain changes
4. Delete the new Flex Consumption app if needed

## References

- [Flex Consumption plan overview](https://learn.microsoft.com/en-us/azure/azure-functions/flex-consumption-plan)
- [How to use the Flex Consumption plan](https://learn.microsoft.com/en-us/azure/azure-functions/flex-consumption-how-to)
- [Azure CLI flex-migration commands](https://learn.microsoft.com/en-us/cli/azure/functionapp/flex-migration) (Linux only)
- [Flex Consumption IaC samples](https://github.com/Azure-Samples/azure-functions-flex-consumption-samples/tree/main/IaC)
- [Flex Consumption plan deprecations](https://learn.microsoft.com/en-us/azure/azure-functions/functions-app-settings#flex-consumption-plan-deprecations)
references/services/redis/
redis-to-amr.md 7.5 KB
# Azure Cache for Redis β†’ Azure Managed Redis (AMR) Migration

> **Target for both paths**: Azure Managed Redis (AMR) β€” M, B, X (Flash), A series
> **Source determines which dedicated skill to install** β€” see decision table below.

There are **two distinct migration paths** to AMR, depending on the source SKU. Each path is owned by a dedicated, versioned skill maintained by the Azure Managed Redis team. The `azure-upgrade` skill does **not** ship the SKU specs, pricing scripts, ARM automation, or template-transformation logic needed for either migration β€” it routes the user to the correct dedicated skill.

## Decision Table β€” Which Skill?

| Source SKU | ARM Resource Type | CLI / PowerShell | Dedicated Skill | Repo |
|---|---|---|---|---|
| **ACR / OSS Redis** β€” Basic, Standard, Premium (C0–C6, P1–P5) | `Microsoft.Cache/redis` | `az redis`, `*-AzRedisCache` | `amr-migration-skill` | https://github.com/AzureManagedRedis/amr-migration-skill |
| **ACRE** β€” Enterprise (`Enterprise_E*`), Enterprise Flash (`EnterpriseFlash_F*`) | `Microsoft.Cache/redisEnterprise` | `az redisenterprise`, `*-AzRedisEnterprise*` | `acre-to-amr-migration-skill` | https://github.com/AzureManagedRedis/acre-to-amr-migration-skill |

> ACR and ACRE are **fundamentally different products** with different ARM resource types, different APIs, and different migration mechanics. Picking the wrong skill will give the user wrong guidance. Always disambiguate before pointing.

## Disambiguation β€” How to Tell ACR from ACRE

Ask the user, or inspect the resource. Any **one** of these signals identifies the source:

**ACR (OSS) indicators** β†’ use `amr-migration-skill`:
- Resource type `Microsoft.Cache/redis` (no "Enterprise" suffix)
- SKU names `Basic`, `Standard`, `Premium`, or sizes `C0`–`C6`, `P1`–`P5`
- CLI: `az redis ...` commands
- PowerShell: `New-AzRedisCache`, `Get-AzRedisCache`, etc.
- DNS suffix `.redis.cache.windows.net`
- Default TLS port `6380`

**ACRE indicators** β†’ use `acre-to-amr-migration-skill`:
- Resource type `Microsoft.Cache/redisEnterprise`
- SKU names starting with `Enterprise_` (e.g. `Enterprise_E10`, `Enterprise_E20`) or `EnterpriseFlash_` (e.g. `EnterpriseFlash_F300`)
- CLI: `az redisenterprise ...` commands
- PowerShell: `New-AzRedisEnterpriseCache`, `Get-AzRedisEnterpriseCache`, etc.
- Default TLS port `10000`
- May reference geo-replication groups (`databases create-replica-link`), Private Endpoints, or Private Link DNS zones

If the user has a **mix of ACR and ACRE** resources, both skills should be installed and run separately on the relevant resources. Most users (~97% of the Azure Cache for Redis fleet) are on ACR; ACRE is the smaller (~3%) but functionally distinct case.

## What Each Skill Provides

### `amr-migration-skill` (ACR/OSS β†’ AMR)

- SKU mapping ACR Basic/Standard/Premium β†’ AMR M/B/X/A with validated SKU specs
- Real-time pricing scripts (PowerShell + bash) with HA, clustering, and MRPP logic
- Cache metrics assessment (default 7-day window) for sizing
- Automated migration via ARM REST APIs with **DNS switching** (old hostname keeps working; port still changes)
- Validate-before-migrate workflow with explicit confirmation on destructive actions
- IaC template migration for ARM, Bicep, and Terraform with worked before/after examples
- Feature comparison and retirement FAQ
- **Retirement deadlines this skill addresses**: ACR Basic/Standard/Premium tiers are being retired β€” for the current retirement and creation-block dates, see the [official ACR retirement announcement](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-retired-tiers).

### `acre-to-amr-migration-skill` (ACRE β†’ AMR)

- Three modes: automation-script update (ARM/Bicep/CLI/PowerShell/Terraform checklist), interactive cache migration with confirmation gates, generic step-by-step guide
- ACRE β†’ AMR SKU resolution via `listSkusForScaling` with fallback mapping table
- In-place migration for standalone caches; create-new-and-swap for geo-replicated caches
- Geo-replication database config parity (eviction policy, modules, etc.)
- Private Endpoint / Private Link migration and DNS zone updates
- Breaking-changes reference (property changes, DNS, API versions)
- Reusable polling scripts (`Poll-MigrationStatus.ps1`, `poll-migration-status.sh`)
- Post-migration ACRE resource cleanup
- **Retirement deadline this skill addresses**: ACRE / Enterprise Flash are being retired β€” for the current retirement date, see the [official ACR retirement announcement](https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-retired-tiers).

## Agent Behavior

When a user asks about migrating any flavor of Azure Cache for Redis to Azure Managed Redis:

1. **Do not attempt the migration from this skill.** Neither SKU specs nor migration automation are inlined here.
2. **Determine the source** using the disambiguation signals above. If unclear, ask the user (or inspect the script/resource).
3. **Point the user to the correct dedicated skill** with its repo URL. The repo READMEs include install instructions for GitHub Copilot, Claude Code, and other compatible hosts.
4. After installation, the user's agent will match the dedicated skill on its trigger phrases (e.g. *"migrate my P2 cache to AMR"*, *"convert my Bicep Redis template"*, *"migrate my Enterprise_E10 cache"*, *"update my ACRE ARM template for AMR"*).
5. If the user has **both** ACR and ACRE resources, recommend installing **both** skills and running them on the relevant resources separately.

## Key Migration Facts (so the agent can set expectations)

These are summarized for awareness. For anything operational, defer to the dedicated skills.

### ACR β†’ AMR
- **Port**: 6380 β†’ **10000**. Non-TLS 6379 not supported on AMR.
- **DNS suffix**: `.redis.cache.windows.net` β†’ `<region>.redis.azure.net`
- **Redis version**: 6 β†’ **7.4**
- **No "shards"** in AMR-facing terminology β€” sharding is internal. Use performance tier (Balanced, Memory Optimized) and size (e.g. B10, M20).
- **Auth**: Recommend adopting **Microsoft Entra ID** post-migration. Entra config is **not** auto-migrated.
- **DNS-switch automated migration** keeps old hostname working, but the port change still applies β€” apps must be updated.

### ACRE β†’ AMR
- **ARM resource type is unchanged** between the ACRE source and AMR target β€” both use `Microsoft.Cache/redisEnterprise`. Property values do change (API version, SKU naming, etc.); see the breaking-changes reference in the dedicated skill.
- **Geo-replicated caches** require create-new-and-swap, not in-place migration.
- **Private Endpoints / Private Link** DNS zones must be updated.
- **Database config parity** (eviction policy, modules) must match across replicas before migration.

## Why this is not implemented inline

Both migrations require maintained SKU tables, live pricing or capability lookups, and ARM-level orchestration that is updated independently of the `azure-skills` release cycle. Keeping that logic in two places would drift. The dedicated skill repos are the single source of truth and are versioned independently.

## References

- AMR Migration Skill (ACR β†’ AMR): https://github.com/AzureManagedRedis/amr-migration-skill
- ACRE β†’ AMR Migration Skill: https://github.com/AzureManagedRedis/acre-to-amr-migration-skill
- Azure Managed Redis docs: https://learn.microsoft.com/en-us/azure/redis/managed-redis/
- ACR retirement announcement: https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-retired-tiers
- Entra ID auth for AMR: https://learn.microsoft.com/en-us/azure/redis/managed-redis/managed-redis-entra-for-access-control-configuration

License (MIT)

View full license text
MIT License

Copyright 2025 (c) Microsoft Corporation.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.