Add retry logic for transient network errors in manage-payg-transition.ps1 - #1
Open
pochiraju wants to merge 48 commits into
Open
Add retry logic for transient network errors in manage-payg-transition.ps1#1pochiraju wants to merge 48 commits into
pochiraju wants to merge 48 commits into
Conversation
…sql-license-type.ps1 - Connect-Azure now reuses an existing valid Az/CLI session matching the target tenant instead of always forcing re-login, preventing hangs in non-interactive contexts. - Module presence check now verifies Az.Accounts >= 4.2.0 directly instead of checking for the Az meta-package, avoiding false negatives and unnecessary/conflicting reinstalls. - Includes pre-existing manage-payg-transition.ps1 fixes: removed unused Force_Start_On_Resources param usage, corrected Arc script download URL path, and cleaned up wrapper argument line-continuation formatting. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… but never fetched) Live end-to-end testing of RunMode Single revealed the Arc/Azure sub-scripts were never downloaded before being invoked via the generated wrapper, causing 'term not recognized' errors. Invoke-RestMethod download calls (matching Invoke-RemoteScript's Scheduled-mode logic) are now added before building the wrapper lines for both Arc and Azure targets. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…e parameter
- Embedded the full logic of modify-azure-sql-license-type.ps1,
modify-arc-sql-license-type.ps1, and set-azurerunbook.ps1 directly in this
script. No external downloads (raw.githubusercontent.com) are required to
run it anymore; the embedded content is materialized to local files at
runtime (required for local invocation and Azure Automation runbook
import), replacing the old Invoke-RestMethod download step.
- Added -TargetLicenseType parameter (PAYG [default] or AHUB) to control
which license model resources are transitioned to. Internally translated
to the vocabulary each embedded script expects:
- Azure SQL resources: LicenseIncluded (PAYG) / BasePrice (AHUB)
- Arc SQL Server: PAYG / LicenseOnly (AHUB-equivalent)
Previously the Arc transition target was hardcoded to PAYG only.
- Updated README to describe the self-contained behavior and the new
parameter.
Verified via live end-to-end testing against rajpoTest (SQL VM):
- Default (-TargetLicenseType PAYG): AHUB -> PAYG succeeded, no downloads.
- -TargetLicenseType AHUB: PAYG -> AHUB succeeded, no downloads.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…est results Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The embedded/standalone set-azurerunbook.ps1 hardcoded './PayTransitionDownloads/' as the folder prefix for -RunbookPath during Import-AzAutomationRunbook, but the outer manage-payg-transition.ps1 materializes embedded scripts to './manage-payg-transition/'. This mismatch caused every -RunMode Scheduled invocation to fail because the runbook file could never be found at the computed path. Found via rubber-duck agent review. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- -Target now defaults to 'Both' instead of being mandatory. - -RunMode now defaults to 'Single' instead of being mandatory. - Updated docstring/examples to reflect the new defaults. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
These parameters are only actually used by the Azure Automation account setup path (RunMode Scheduled). They were previously mandatory unconditionally, which forced -RunMode Single (one-time run) callers to supply unused values. Made both optional in the param block and added a runtime check that requires them only when -RunMode is 'Scheduled', failing fast with a clear error otherwise. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…apper The Single-mode command-line generator emitted boolean arg values (e.g. the hardcoded Force = $true for Arc) as a separate quoted token: '-Force 'True''. Since -Force is a [switch] parameter in the embedded script, PowerShell does not bind that trailing 'True' token to the switch; instead it gets consumed as an unbound positional argument, which binds to the script's first positional parameter (-SubId). This caused failures such as: 'Subscription True was not found in tenant ... Please verify that the subscription exists in this tenant.' Fixed by detecting boolean-valued args and emitting them as a bare switch (e.g. '-Force') with no value when true, and omitting them entirely when false, instead of always emitting '-ArgName ''<value>'''. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The manage-payg-transition.ps1 wrapper exposed no -TenantId or -ReportOnly parameters, so operators could not target a specific tenant explicitly or perform a safe dry run. The embedded scripts already accept both; only the wrapper pass-through was missing. Wired through for Single and Scheduled modes. Also fixed a genuine bug in modify-arc-sql-license-type.ps1: the 'Found N resource(s) to update' message read $resources.Count before $resources was populated by the paging loop, so it always reported 0 even when resources were found. Moved the message after the loop and switched it to $allResults.Count. Applied to both the standalone script and the copy embedded in manage-payg-transition.ps1. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Set-AzConnectedMachineExtension is called with -NoWait and previously had no -ErrorAction, so service-side failures (e.g. 'An extension of type ... is still processing. Only one instance of an extension may be in progress at a time') surfaced as non-terminating errors. The catch block was never entered, and the script printed 'Updated -- ...' for resources that had in fact failed. Add -ErrorAction Stop so those errors are caught, and record the real outcome per resource in the CSV via new UpdateResult/UpdateError columns (NotAttempted / RequestSubmitted / Failed). The error text is now included in the console message as well. Applied to both the standalone modify-arc-sql-license-type.ps1 and the copy embedded in manage-payg-transition.ps1; verified the two blocks are identical. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The parameter table and all three examples documented parameters that do not exist on manage-payg-transition.ps1 (-SubId, -ResourceGroup, -RunAt, -AutomationAccount, -ExclusionTag). Every documented example would have failed if a user copied it. Replaced with the real parameter names (-targetSubscription, -targetResourceGroup, -RunMode, -AutomationAccResourceGroupName, -AutomationAccountName) and documented the previously undocumented -Target, -TenantId, -ReportOnly, -cleanDownloads. Also: - Added a dry-run example and documented -ReportOnly as the recommended first step. - Documented implicit tenant selection and the new UpdateResult/UpdateError CSV columns. - Noted that already-converged resources are excluded by design (idempotent re-runs) and that Disconnected/Expired Arc agents cannot be updated. - Fixed Cloud Shell auth step to use Connect-AzAccount instead of Connect-AzureAD. Verified every parameter appearing in a README example resolves against the script's AST parameter block. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Each run materializes the embedded dependency scripts into ./manage-payg-transition/, writes a generated invocation wrapper to runnow.ps1, and emits a ModifiedResources_<timestamp>.csv report plus a transcript log. These were previously left untracked in the sample folder and could easily be committed by accident. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolves the previously blocked Arc test case (microsoft#6) - Single/Arc has now been executed end-to-end against real Arc-enabled machines. Adds cases microsoft#12-microsoft#19 covering the -Force switch-binding fix, -TenantId/-ReportOnly pass-through, the always-zero resource count fix, isolated-folder self-containment, embedded vs standalone sync, truthful Arc update outcomes, idempotent re-runs, and the README parameter cross-check. Refreshes Known gaps: Scheduled mode still never executed end-to-end; Disconnected/Expired Arc agents cannot be updated; arcdata SqlServerInstances with hostType 'Azure Virtual Machine' are read-only mirrors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Both license scripts called Start-Transcript unconditionally and matched it with an unguarded Stop-Transcript. If transcription never started -- because the log path is not writable, or because the host does not support transcription (Azure Automation runbooks do not) -- the script ended with: Stop-Transcript: An error occurred stopping transcription: The host is not currently transcribing. The run itself had already succeeded, so this surfaced a spurious failure at the very end of an otherwise clean execution. Track whether transcription actually started and only stop it in that case, warning instead of throwing if the start fails. Applied to modify-arc-sql-license-type.ps1, modify-azure-sql-license-type.ps1, and both copies embedded in manage-payg-transition.ps1. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
If the SQL server query returned nothing, the script fell back to scanning every server in the subscription whenever -ResourceName was absent -- even when -ResourceGroup had been supplied. The elastic pool query is not resource-group filtered (it only filters on licenseType and tags), so a real run could have modified elastic pools on servers entirely outside the requested resource group. Observed live: a run scoped to 'rajpobuddy' went on to scan three servers in PT_Bugbash_Databases, BinujRg and TestServersSi6ci1WestEuropeRG. No pools existed there, so nothing was wrongly modified, but the exposure was real. Only fall back to all servers when neither -ResourceName nor -ResourceGroup was specified; otherwise skip database/elastic pool processing and say so. Also fix two misleading messages that made a successful dry run look like a no-op: - The SQL VM path printed nothing in ReportOnly mode, so a resource found and recorded in the CSV appeared to be silently ignored. It now reports the transition it would have made. - 'No SQL VMs found to start that require a license update' printed on every run because is never populated. Reworded to describe what it actually means. Applied to the standalone script and re-synced the embedded copy. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… verified end-to-end PAYG run Case microsoft#21 records the scope-escape regression test: with -targetResourceGroup set but no SQL Servers in that group, the script no longer falls back to scanning every server in the subscription (which could have modified out-of-scope elastic pools, since that query has no resource-group filter). Case microsoft#22 records the final real run, with the resulting license type verified out-of-band via 'az sql vm show' and Search-AzGraph rather than trusting the script's own log output. Also documents two newly-noted gaps: fixed transcript log paths overwrite prior runs, and the Azure-side CSV lacks the UpdateResult/UpdateError columns. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The integration runtime filter selected any runtime with Type 'Managed' whose LicenseType differed from the target. The default AutoResolveIntegrationRuntime is also Type 'Managed' (it is the data-flow runtime, not an SSIS-IR) and has a null LicenseType, so 'null -ne LicenseIncluded' selected it. The subsequent Set-AzDataFactoryV2IntegrationRuntime round-tripped the full payload and the service rejected it with: Conflict / DataFactoryPropertyUpdateNotSupported Updating property managedVirtualNetwork is not supported The filter now also requires a non-empty LicenseType, which only SSIS integration runtimes carry. The failure was additionally reported as a success: the cmdlet had no -ErrorAction Stop, so the Conflict was non-terminating, the enclosing catch never fired, and the script printed '-- DataFactory ... updated to license type LicenseIncluded' directly after two error blocks. Each runtime is now updated inside its own try/catch with -ErrorAction Stop. Applies the same result-checking to the SQL VM path, which piped 'az sql vm update' straight into ConvertFrom-Json without checking LASTEXITCODE and appended its CSV row before the attempt. Rows are now projected onto an explicit column set at export time, because Export-Csv derives its header from the first object only and would otherwise drop UpdateResult/UpdateError depending on which section emitted first. Adds TESTPLAN cases microsoft#23-microsoft#26 and re-syncs the embedded copy in the self-contained orchestrator. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The result-checking added for SQL VMs covered only one of five 'az ... update' call sites. Managed Instances, SQL Databases, elastic pools and instance pools still piped the CLI output straight into ConvertFrom-Json without inspecting LASTEXITCODE, and appended their CSV row before the attempt, so a failed update was recorded identically to a successful one. The elastic pool path was the worst case: az sql elastic-pool update ... 2>\ | ConvertFrom-Json -ErrorAction SilentlyContinue which discarded the service error text entirely and reported only 'No result returned', giving no indication of why the update failed. All five paths now route through a shared Invoke-AzCliLicenseUpdate helper that checks LASTEXITCODE, surfaces the real service error, and returns a result object used to populate UpdateResult/UpdateError. The helper initially wrote its success message with Write-Output from inside the function, which in PowerShell merges into the return value: callers received a two-element array rather than the result object, and the message never reached the transcript. The helper is now silent on the success stream and each caller logs its own line. Adds TESTPLAN cases microsoft#27-microsoft#28 and re-syncs the embedded copy. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The report's UpdateResult column does not mean the same thing on both paths, and nothing said so. Azure CLI update calls are synchronous: no --no-wait is passed, so the CLI polls the underlying PUT to a terminal state before returning. Measured on a SQL VM: the call returned after 126s carrying provisioningState 'Succeeded', and the new license type was immediately readable. 'Updated' therefore means committed. The Arc path uses Set-AzConnectedMachineExtension -NoWait and never polls, so 'RequestSubmitted' means only that the service accepted the request. The agent-side push can still fail afterwards without the script observing it. Documents the distinction in the README, adds a Resource Graph query for confirming the real Arc end state, and records TESTPLAN cases microsoft#29-microsoft#30 along with the option of an opt-in -WaitForCompletion switch. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Arc extension updates are submitted with -NoWait, so the report could only ever record 'RequestSubmitted' - the service accepted the request, which says nothing about whether the agent applied it. Confirming the real outcome previously required a separate Resource Graph query. Adds -WaitForCompletion (with -WaitTimeoutSeconds, default 300) which polls each extension to a terminal provisioning state and records Succeeded/Failed/TimedOut instead. The helper also compares the applied LicenseType against the requested value, so a 'Succeeded' provisioning state carrying the wrong license is reported as a failure rather than a success. Polling backs off 5->30s. A timeout is deliberately not treated as a failure: the agent may still apply the setting after the script stops waiting, so the outcome is recorded as inconclusive. The switch is opt-in because it serialises what is otherwise a fast parallel fan-out across potentially hundreds of machines. Default behaviour is unchanged. Verified live against sqltvm: reported Succeeded in 79.6s, confirmed independently via Search-AzGraph (LicenseType LicenseOnly, provisioningState Succeeded). Also verified inert under -ReportOnly and that the wrapper emits it as a bare switch. Adds TESTPLAN cases microsoft#31-microsoft#32. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Azure SQL updates previously always blocked until the operation completed, so a large estate was processed serially. They are now submitted with --no-wait and reported as 'RequestSubmitted'; passing -WaitForCompletion omits the flag so the CLI polls to a terminal state and the result is reported as 'Updated'. This matches the Arc path, which has always used -NoWait, giving one consistent default across resource types. Two resource types cannot honour this and are documented rather than faked: 'az sql vm update' and Set-AzDataFactoryV2IntegrationRuntime expose no --no-wait/-AsJob equivalent. A generic 'az resource update/patch' fallback does not support --no-wait either, and a direct ARM PATCH on a SQL VM is rejected with MissingPatchParameters (approved values: tags, additionalVmPatch), so the license type cannot be set by a lightweight non-blocking call. Hand-rolling a full PUT was judged worse than documenting the limitation. The helper only appends --no-wait for commands that accept it, and tolerates the empty response body those calls return. Adds TESTPLAN cases microsoft#33-microsoft#35 and re-syncs the embedded copy. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
'az sql vm update' has no --no-wait option and blocks for roughly two minutes per VM, so SQL virtual machines were the one Azure resource type that ignored the async-by-default behaviour introduced with -WaitForCompletion. Update-AzSqlVM advertises -NoWait and -AsJob but both are broken in Az.SqlVirtualMachine 2.4.0: -NoWait forwards the bound parameter into Get-AzSqlVM, which rejects it, and -AsJob throws a NullReferenceException. Add Invoke-SqlVmLicenseUpdate, which submits the change to ARM directly by reading the resource and writing it back with only sqlServerLicenseType changed. ARM accepts the request and returns an Azure-AsyncOperation header without waiting, so the call completes in seconds. The synchronous CLI path is retained for -WaitForCompletion and as an automatic fallback on failure. Verified live against rajpoTest: default run reported RequestSubmitted in 49.7s and the change was confirmed in Azure; -WaitForCompletion reported Updated in 189.3s. Re-synced the embedded orchestrator copy and corrected the README behaviour matrix and TESTPLAN case microsoft#34, which wrongly recorded the SQL VM limitation as unavoidable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When an Arc machine already carries a LicenseType it is only overwritten if -Force is supplied. That is intended, but the run was silent about it: the unrelated ConsentToRecurringPAYG block could still set WriteSettings, so the script wrote other settings and printed 'Updated' and 'Confirmed ... Succeeded' for a license type that had not changed. The wait helper could not detect this because it is passed the unmodified settings value, so expected and applied matched. Found while restoring sqltvm, which reported success yet remained LicenseOnly. Emit an explicit warning naming the current and requested license types and pointing at -Force. -Force semantics are unchanged. Also harden Wait-ArcExtensionProvisioning against a stale terminal state. Updates are submitted with -NoWait, so the first poll can observe the previous operation's 'Succeeded' before the new one begins, with the old license type still in place, which was reported as a hard failure. A terminal state carrying an unexpected license type is now inconclusive and polling continues; the mismatch is only returned as Failed if it survives to the timeout. The observed state sequence Updating -> Creating -> Succeeded over ~2.5 minutes confirms the race window is real. Verified live: without -Force the run now reports 'Write Settings - False' and no success line; with -Force sqltvm was restored to PAYG and independently confirmed via Get-AzConnectedMachineExtension. Embedded Arc block re-synced. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Discovery calls piped az straight into ConvertFrom-Json with no exit-code check. The Azure CLI signals failure through \0 rather than by throwing, so a failed query yielded \ and every caller read that as 'no resources found'. A transient failure was therefore indistinguishable from an empty result: the run printed 'Found a total of 0 databases' and 'No SQL Databases found ... that require a license update' and moved on, silently skipping resources that needed transitioning while still looking clean. Observed in a real run where az sql db list returned ResourceGroupNotFound for three servers. The errors were spurious: all four resource groups exist, one server succeeded in the same resource group where another failed, and all three listed their databases correctly on retry. Route every discovery path through a new Invoke-AzCliQuery helper (SQL VM list, VM power state, MI list, server list, database list, elastic pool list, instance pool list). It checks the exit code, surfaces the service error as a warning and normalises the value to an array. Callers that cannot proceed without the data now skip with an explicit warning instead of reporting zero. This is the same bug class already fixed for the update paths. Verified: helper unit-tested across success, failure and empty; a re-run now reports 1 database each for demo731 and sqldbinlinemigtestserver instead of 0. Embedded orchestrator copy re-synced. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Every az call in the Azure script is scoped by the CLI's active subscription, but 'az account set --subscription' was issued without checking \0 at either call site. If the switch failed, the whole subscription loop body, including the update calls, would have continued against whichever subscription was selected beforehand, so resources in an unintended subscription could have been modified. The primary call site now warns and moves to the next subscription. The mid-loop re-selection throws instead, which the enclosing handler catches so SQL Server, database and elastic pool processing is skipped without aborting the run. Audited the remaining unchecked 'az ... | ConvertFrom-Json' pipes: the az tag list one sits inside a commented-out block and the rest are az account show login probes that are already guarded. Regression-verified with a ReportOnly run: all 6 servers and their per-server database counts are still enumerated correctly. Embedded copy re-synced. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds test cases 43 and 44 covering a run that completed with no errors after the discovery-query and subscription-context fixes. Every 'nothing found' claim in that run was checked against Azure rather than taken at face value: the two real databases are already at the target license type and the rest are master databases with a null licenseType; the subscription's single SQL VM and single managed instance are both already at their target, so they are correctly excluded by the filters rather than skipped; and there are no elastic pools, instance pools or Arc machines. Case 44 validates the DataFactory SSIS filter at a scale not previously tested: 22 integration runtimes across 8 factories, all with an empty LicenseType, including 5 of type Managed. Under the original filter all 22 would have been selected and the Managed ones would have failed with the same managedVirtualNetwork Conflict that motivated the fix. Also narrows the async coverage gap note: bhrout-mi exists but is already at the target and belongs to another team, so it was left alone. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Verified via 'git archive' that a merge containing only the manage-payg-transition folder delivers a self-sufficient orchestrator: it materializes both sub-scripts from its embedded here-strings and runs end-to-end with the sibling scripts absent, and the materialized copies are byte-identical to the fixed standalone scripts. Records the caveat that a folder-only merge would leave the standalone modify-azure-sql-license-type.ps1 and modify-arc-sql-license-type.ps1 stale on master and break the embedded-vs-standalone sync invariant. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Map -TargetLicenseType AHUB to 'Paid' (License with Software Assurance / Azure Hybrid Benefit) for Arc SQL Server rather than 'LicenseOnly' (perpetual without SA), allowing ESU to remain enabled and correctly aligning with AHB. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Implemented Format-ExecutionOutcomeSummary across Azure SQL, Arc SQL, and manage-payg-transition scripts. - Summarizes qualified, updated, failed, and skipped resource counts grouped by friendly SQL resource type. - Added detailed Failure & Skip Root Causes table reporting resource name, resource group, outcome, and diagnostic root cause reason. - Updated TESTPLAN.md with test case microsoft#46 documenting the outcome summary and verification. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tatuses - Wrapped Where-Object in @(...) to guarantee integer count in PowerShell. - Added 'RequestSubmitted' and 'Succeeded' to the Azure script summary matching set. - Verified live table formatting now correctly displays Updated counts. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Renamed 'Updated' column to 'Updated or RequestSubmitted'. - Set first column header to 'ResourceType' and sorted table by ResourceType. - Consolidated summary output to print once at the very end of orchestrator execution instead of separate intermediate summaries. - Updated sub-scripts to support -NoSummary when called by orchestrator. - Updated TESTPLAN.md test case microsoft#46. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… summary - Clean stale tracked_*.json files before starting Single run and after aggregating summary. - Ensure sub-scripts remove tracked_*.json if no resources were marked for modification so prior runs do not contaminate subsequent runs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…1, modify-azure-sql-license-type.ps1, runnow.ps1)
…n.ps1 - Add Invoke-AzCliArgsWithRetry/Invoke-AzCmdletWithRetry helpers that retry transient network failures (socket exhaustion, HttpRequestException, timeouts) with backoff. - Use retry wrapper in Invoke-AzCliLicenseUpdate and Invoke-AzCliQuery. - Fix DataFactory SSIS section: Set-AzContext/Get-AzDataFactoryV2/ Get-AzDataFactoryV2IntegrationRuntime now use -ErrorAction Stop + retry so a transient failure is no longer silently mistaken for 'no integration runtimes found' or left running against the wrong subscription context. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Get-AzSubscription -SubscriptionId / Get-AzSubscription had no -ErrorAction Stop, so a transient HttpRequestException (seen repeatedly outside the dev environment) was a non-terminating error: \ stayed empty, the foreach loop over subscriptions ran zero times, and the script printed a clean-looking 'No resources were marked for modification' summary instead of surfacing the failure. Fixed in both the Arc and Azure sections: retry up to 3x with a 5s backoff, then exit 1 with a clear error if subscription resolution still fails, and abort if the resolved subscription list is empty for any other reason. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…sition blips Real-world runs showed the Arc script exhausting 3 retries (5s each, ~10s total) on 'Unable to acquire token ... An error occurred while sending the request' immediately before the very next (Azure) invocation succeeded with no retry needed at all, confirming these are short-lived (10-30s) transient blips rather than a hard block. Increased to 5 attempts with increasing backoff (5/10/20/30/30s, ~95s total) in both the Arc and Azure sections so the script has a realistic chance to ride out the blip instead of exiting before it clears. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Get-AzSubscription was being called without -TenantId, so for a multi-tenant signed-in account (guest access to other tenants), it fanned out and tried to acquire a token for *every* tenant the account belongs to in order to resolve the subscription -> tenant mapping. This produced unrelated 'Authentication failed against tenant ... conditional access ... MFA' warnings for guest tenants even when a specific -TenantId was passed to the script, and made the whole call more fragile since it depended on tenants the user never intended to touch. Added -TenantId \ to both Get-AzSubscription call sites (single-subscription and list-all) in both the Arc and Azure sections, scoping resolution strictly to the requested tenant. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…aph, az CLI) - Arc section: auto-install Az.ConnectedMachine/Az.ResourceGraph on demand (mirroring the existing Az.DataFactory pattern) instead of silently continuing after a failed Import-Module, which left Search-AzGraph undefined and crashed later with an unrelated ArgumentNullException. - Guard Search-AzGraph's result against \ before AddRange to avoid 'Value cannot be null' if the module still can't be resolved. - Azure section: fail fast with an actionable error if the 'az' CLI isn't installed, instead of letting every downstream az call fail later with a confusing 'term az is not recognized' error mid-run. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Attempts a silent 'winget install Microsoft.AzureCLI' when 'az' isn't on PATH, refreshing the process PATH afterwards so a fresh install can be picked up without restarting the shell. Falls back to the existing clear, actionable error (with a manual install link) if winget isn't available or the install fails. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…sence Replaces the single winget attempt with a real self-healing install path: 1. Try 'winget install Microsoft.AzureCLI' and verify az is now resolvable. 2. If winget is unavailable or didn't work, fall back to downloading and silently running the official Azure CLI MSI (aka.ms/installazurecliwindows) via msiexec, which doesn't depend on winget being present. 3. Only after both real install attempts fail does the script report the actionable manual-install error and exit - it no longer gives up after a single unverified winget call. Adds a shared Refresh-Path helper to reload PATH from Machine/User scopes after each install attempt so a freshly installed az.cmd is found in the same process without restarting the shell. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds a single pre-connect step in the top-level (non-embedded) part of the script for -RunMode Single: establishes the Az PowerShell context and, when targeting Azure, verifies/logs in the Azure CLI exactly once before invoking the Arc and Azure sub-scripts. Sets \ / \ so the embedded Azure script's Connect-Azure short-circuits its own Azure CLI install-check/login entirely when the parent already verified it for the same tenant, instead of repeating that work. Falls back to the original full connect logic unchanged when run standalone (e.g. as an Azure Automation runbook) where these env vars won't be set. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Rewrite modify-azure-sql-license-type.ps1 (embedded) to use Az PowerShell cmdlets (Get-/Set-AzSqlVM/Instance/Database/ElasticPool/InstancePool, Set-AzDataFactoryV2IntegrationRuntime) instead of Azure CLI, addressing review feedback to drop the CLI dependency. - Add explicit Az.Sql / Az.SqlVirtualMachine module ensure/install/import logic (previously relied on implicit autoloading, which fails when the modules are not already installed). - SQL VM license updates remain fully synchronous (Update-AzSqlVM), since -NoWait/-AsJob are broken in Az.SqlVirtualMachine 2.4.0; other resource types keep the async default via -AsJob, mirroring the previous --no-wait behavior. - Update README.md to reflect CLI-free prerequisites and current synchronous/async behavior per resource type. - Update TESTPLAN.md with a new Round 2 section documenting live validation of the CLI removal across two subscriptions (SQL VM, Managed Instance, SQL Database), the Az.Sql/Az.SqlVirtualMachine regression and fix, and refresh the Known gaps / Required permissions sections for the PowerShell-only implementation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
TESTPLAN.md records live test results against real Microsoft-internal subscriptions, resource names, tenant IDs, and account details. Per Travis's review feedback, remove it from source control and add it to .gitignore so this internal information is not published in the public repo. The file remains locally as a development record; it is simply no longer tracked. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Prompt before installing Az modules interactively; add -Force to skip prompts for non-interactive use - Remove Arc/Azure distinction wording when confirming account/tenant context; confirm interactively or require -Force - Simplify connection messages to 'Connecting to Azure...' and 'Reusing this session' - Fix 'SQL Servers' -> 'SQL servers' for Azure SQL DB logical server references - Remove stale 'mirrors the previous CLI' comments - Replace 'RG' abbreviation with 'resource group' throughout output messages - Remove SSIS/Data Factory integration runtime support entirely from the script and README - Simplify README SQL VM synchronous-behavior explanation to one sentence Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
While running manage-payg-transition.ps1 outside the usual dev environment, some Azure CLI / Az PowerShell calls failed with transient network errors (Windows ephemeral port exhaustion / WinError 10048, HttpRequestException). This PR adds retry-with-backoff for those transient failures and fixes a correctness bug in the DataFactory SSIS section.
Changes
Invoke-AzCliArgsWithRetry(az CLI) andInvoke-AzCmdletWithRetry(Az PowerShell cmdlets) helpers that retry up to 3x with a 5s backoff, but only for transient error patterns (socket exhaustion/10048, connection reset, timeout, 429/5xx).Invoke-AzCliLicenseUpdateandInvoke-AzCliQuery.Set-AzContext,Get-AzDataFactoryV2, andGet-AzDataFactoryV2IntegrationRuntimepreviously had no-ErrorAction Stop, so a transient failure was silently treated as no integration runtimes found (and a failed context switch could leave later calls running against the wrong subscription). These now use-ErrorAction Stop+ retry, and a genuine query failure is reported asUpdateResult = Failedin the CSV report instead of a false negative.Validation
ratruong-test-sqldb's update failed with WinError 10048; the failure path now retries automatically instead of failing permanently on the first transient blip./cc @twright-msft