Skip to content

FEATURE: apiops compare to compare two apim instances #22

Description

Compare should compare two API Management instances and report back the differences between the two. Users can use the compare command to verify deployments or investigate differences in different environments.

We should use compare-apiminstance.ps1 as a guide. It contains some of the nuances needed, like dealing with auto-generated identifiers.

Activity

  1. EMaher commented on May 4, 2026

    @EMaher
    ContributorAuthor

    Implementation Plan: apiops compare Command

    Problem Statement

    Add an apiops compare command that compares two Azure API Management instances via the ARM REST API and reports differences. This enables users to verify deployments or investigate environment drift. The PowerShell script at tests/integration/all-resource-types/Compare-ApimInstance.ps1 serves as the reference implementation with all the special-case normalization logic.

    Approach

    Follow the existing command pattern (extract, publish, init) — a Commander subcommand, a service orchestrator, and shared infrastructure. The core challenge is normalization: two APIM instances will have different instance-specific values (subscription IDs, resource group names, service names, auto-generated IDs, timestamps, secrets) that must be neutralized before comparison.


    Special Cases from PowerShell Reference

    These are the normalization rules discovered from Compare-ApimInstance.ps1 that must be ported:

    1. Stripped Fields

    • Top-level ARM envelope: id, type, name, systemData, etag — always removed
    • Read-only properties (root properties only): provisioningState, createdAtUtc, lastModifiedDate, isCurrent, isOnline, stateComment, createdDate
    • Timestamp properties (any depth): lastStatus, specificationLastUpdated, createdDateTime, updatedDateTime

    2. Instance-Specific String Normalization

    • ARM resource ID paths: /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.ApiManagement/service/{name} → placeholder
    • Broader subscription/RG normalization for non-APIM resource references
    • APIM service name in any remaining string positions
    • Key Vault URIs: https://{vault}.vault.azure.net → placeholder
    • Key Vault secret name prefixes (e.g., src- vs tgt-) → placeholder
    • Application Insights component names in resource IDs → placeholder
    • Event Hub namespace names in resource IDs → placeholder
    • Auto-generated APIM IDs (24-char hex) → {{auto-id}}
    • GUIDs (8-4-4-4-12) → {{guid}}

    3. Request/Response & Representation Handling

    • On objects with a representations array: ignore description (WSDL/spec import generates varying descriptions)
    • On representation objects (have contentType or schemaId): ignore description, schemaId, typeName (spec-based publish doesn't populate these)

    4. Auto-Generated ID Matching

    • Resources with 24-char hex names OR UUID-format names are auto-generated by APIM
    • After extract→publish, APIM creates new IDs, so names never match
    • Solution: key by sorted normalized content using positional keys {{auto-id-0}}, {{auto-id-1}}, etc.

    5. Resource-Specific Skip Rules

    • Secret named values: skip .properties.value comparison (secrets aren't extractable)
    • Event Hub / App Insights loggers: skip .properties.credentials (connection strings differ per instance)
    • Built-in exclusions: Groups administrators, developers, guests; Products starter, unlimited; Subscriptions master; APIs echo-api

    6. Hierarchical Comparison

    • Top-level resources compared first
    • Then child resources enumerated from source: API children (operations, policies, schemas, tags, diagnostics, resolvers, releases, wikis, tag descriptions), operation policies, resolver policies
    • Product children (policies, APIs, groups, tags, wikis)
    • Gateway children (APIs)
    • Workspace children (apis, products, backends, namedValues, tags, groups, policyFragments, schemas, loggers, diagnostics, policies, subscriptions, apiVersionSets)

    Implementation Todos

    ID Task File(s)
    T-CMP-06 Add CompareConfig to config types src/models/config.ts
    T-CMP-03 Implement normalization module (reuses resource-path.ts / resource-uri.ts utilities) src/lib/compare-normalizer.ts
    T-CMP-04 Implement diff engine src/lib/compare-differ.ts
    T-CMP-05 Implement compare service orchestrator src/services/compare-service.ts
    T-CMP-01 Add compare CLI command registration src/cli/compare-command.ts
    T-CMP-07 Add text/JSON output formatting src/cli/compare-command.ts
    T-CMP-02 Register compare command in index.ts src/cli/index.ts
    T-CMP-08 Unit tests for normalizer tests/unit/compare-normalizer.test.ts
    T-CMP-09 Unit tests for differ tests/unit/compare-differ.test.ts
    T-CMP-10 Unit tests for compare service tests/unit/compare-service.test.ts
    T-CMP-11 Update CLI commands spec doc specs/contracts/cli-commands.md

    Key Design Decisions

    • Reuse resource-path.ts: The normalizer must NOT reimplement ARM path parsing or template logic. It uses formatTemplatePath, parseTemplatePath, getNameFromNameParts, isSingletonType, isChildType, deriveListPaths from existing infrastructure.
    • Reuse resource-uri.ts: URL construction via buildArmUri() and labels via buildResourceLabel().
    • Reuse resource-types.ts: The existing data model (ResourceType enum, RESOURCE_TYPE_METADATA) already covers all 33 resource types — no new model needed.
    • Reuse IApimClient: listResources() and getResource() are sufficient for comparison; no client interface changes needed.

    CLI Interface

    apiops compare \
      --source-resource-group <rg> --source-service-name <name> \
      --target-resource-group <rg> --target-service-name <name> \
      [--target-subscription-id <id>] [--source-subscription-id]
    
    • Can either use --subscription-id OR ( --target-subscription-id <id> and --source-subscription-id)
    • Comparison must be in same tenant and same cloud.
    • Inherits global options: --subscription-id, --cloud, --format, --log-level, auth flags
    • Exit codes: 0 = identical, 1 = differences found, 2 = fatal error

    Dependency Order

    T-CMP-06 (config type)
      ↓
    T-CMP-03 (normalizer) → T-CMP-08 (normalizer tests)
      ↓
    T-CMP-04 (differ) → T-CMP-09 (differ tests)
      ↓
    T-CMP-05 (service) → T-CMP-10 (service tests)
      ↓
    T-CMP-01 (CLI command) + T-CMP-07 (output formatting)
      ↓
    T-CMP-02 (register in index.ts)
      ↓
    T-CMP-11 (docs update)
    
  2. github-actions commented on May 7, 2026

    @github-actions

    🏗️ Squad Triage — ApiOpsLead (🏗️ Tech Lead)

    Issue: #22 — SPEC ISSUE: Design apiops compare to compare two apim instances
    Assigned to: ApimExpert (🔵 APIM Expert)
    Reason: Issue relates to backend/API work

    Team roster:

    • ApiOpsLead (🏗️ Tech Lead) → label: squad:apiopslead
    • ApimExpert (🔵 APIM Expert) → label: squad:apimexpert
    • ApicExpert (🟣 APIC Expert) → label: squad:apicexpert
    • TypeScriptDev (🔷 TypeScript Developer) → label: squad:typescriptdev
    • NodeJsDev (🟢 Node.js Developer) → label: squad:nodejsdev
    • TestEngineer (🧪 Test Engineer) → label: squad:testengineer
    • OpenSourceExpert (⚖️ Open Source Expert) → label: squad:opensourceexpert
    • CodeReviewer (🔍 Code Reviewer & Standards Enforcer) → label: squad:codereviewer
    • AzdoExpert (🔶 Azure DevOps Expert) → label: squad:azdoexpert
    • GitHubExpert (⚫ GitHub Expert) → label: squad:githubexpert
    • Ralph (🔄 Work Monitor) → label: squad:ralph

    To reassign, remove the current squad:* label and add the correct one.

  3. linked a pull request that will close this issuefeat: implement `apiops compare` command #43on May 8, 2026
  4. changed the title [-]SPEC ISSUE: Design `apiops compare` to compare two apim instances[/-] [+]FEATURE: `apiops compare` to compare two apim instances[/+] on May 29, 2026
  5. added
    staleNo activity for 30+ days — will be closed if no further activity
    on Jun 29, 2026
  6. github-actions commented on Jun 29, 2026

    @github-actions

    ⚠️ Stale issue notice — This issue has had no activity for 31 day(s).

    It will be automatically closed after 60 days of inactivity.

    To keep this issue open:

    • Leave a comment describing the current status, or
    • Remove the stale label.

    This notice was posted automatically by the stale issue workflow.

  7. added
    P2Next sprint
    and removed
    staleNo activity for 30+ days — will be closed if no further activity
    on Aug 27, 2026
  8. github-actions commented on Sep 27, 2026

    @github-actions

    ⚠️ Stale issue notice — This issue has had no activity for 30 day(s).

    It will be automatically closed after 60 days of inactivity.

    To keep this issue open:

    • Leave a comment describing the current status, or
    • Remove the stale label.

    This notice was posted automatically by the stale issue workflow.

  9. added
    staleNo activity for 30+ days — will be closed if no further activity
    on Sep 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

P2Next sprintgo:needs-researchNeeds investigationsquadSquad triage inbox — Lead will assign to a membersquad:apimexpertAssigned to ApimExpert (🔵 APIM Expert)staleNo activity for 30+ days — will be closed if no further activity

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions