Fix change-safety dynamicparam forwarding for custom-fronted cmdlets - #1555
Merged
Yabo Hu (VeryEarly) merged 2 commits intoAug 13, 2026
Conversation
The outer generated proxy for custom-fronted cmdlets (a hand-written function in custom/ instead of a private cmdlet) never emitted a dynamicparam forwarding block, so change-safety parameters declared on the wrapped custom function's own dynamicparam block never surfaced on the public command. HasDynamicParameters() now also detects function-backed variants whose body declares a dynamicparam block (previously only IDynamicParameters on a compiled cmdlet was checked). The outer proxy's GetCommand lookup now resolves both Cmdlet and Function command types so it can reflect the wrapped custom function's dynamic parameters.
YangAn-microsoft
added a commit
to YangAn-microsoft/azure-powershell
that referenced
this pull request
Aug 12, 2026
…ile cmdlets Regenerated Az.Cdn with the autorest.powershell generator fix (see Azure/autorest.powershell#1555): the outer proxy for custom-fronted write cmdlets (New/Remove/Update-Az{,FrontDoor}CdnProfile) now forwards the wrapped custom function's dynamicparam block, so -AcquirePolicyToken and -ChangeReference actually bind on these commands. Previously they were rejected with 'A parameter cannot be found that matches parameter name AcquirePolicyToken', even though the module was already opted into enable-change-safety. Live-validated: New-AzFrontDoorCdnProfile -AcquirePolicyToken now successfully creates a profile in a real subscription.
The BuildTime runtime source (PsProxyOutputs.cs) is copied as a shared resource into every generated test-emitter case, so the previous commit's fix changes generated/runtime/BuildTime/Models/PsProxyOutputs.cs in all 43 whitelisted cases. Refreshed via EmitterTest.ps1 -AllowList and copied each case's regenerated PsProxyOutputs.cs over its committed target/ baseline (same process as commit 0ef7785).
Yabo Hu (VeryEarly)
approved these changes
Aug 13, 2026
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
Fixes a gap in the Stage D change-safety generator work (#1549): the outer generated proxy for custom-fronted cmdlets (cmdlets whose public implementation is a hand-written function in
custom/rather than the generated private cmdlet) never emitted adynamicparamforwarding block. As a result, change-safety parameters (-AcquirePolicyToken/-ChangeReference) declared on the wrapped custom function's owndynamicparamblock never surfaced on the public command, and binding them failed with 'A parameter cannot be found that matches parameter name...'.Root cause
DynamicParamOutput.HasDynamicParameters()only checked whether a variant's compiled cmdlet implementedIDynamicParameters. For a custom-fronted cmdlet, the wrapped command is a PowerShell function, not a compiled cmdlet, so this check always returned false and no forwarding block was emitted on the outer proxy — even though the custom function itself already had its owndynamicparamblock (added separately).Fix
HasDynamicParameters()now also inspects function-backed variants: unwrapsFunctionInfo.ScriptBlock.Ast(aFunctionDefinitionAst) to itsBody(ScriptBlockAst) and checks.DynamicParamBlock != null.$ExecutionContext.InvokeCommand.GetCommand(...)lookup now resolves bothCmdletandFunctioncommand types so it can reflect a custom function's dynamic parameters, not just a compiled cmdlet's.Validation — live test evidence
Consuming PR: Azure/azure-powershell#29999 (Az.Cdn). Rebuilt
Az.Cdn.private.dllwith this fix, regeneratedexports/*.ps1, and tested against a real Azure subscription (f758ac53-3e63-4317-a956-0997793808d7, tenant4f00b3b6-2940-4f2c-b037-94637c180d30) — not mocked/recorded.Before the fix:
Confirmed root cause — the outer public proxy had no
dynamicparamblock at all:After the fix:
The profile was created successfully end-to-end, confirming
-AcquirePolicyTokennow binds and the change-safety token acquisition path executes — the same wire behavior already validated for the generated, non-custom cmdlet path (e.g.New-AzFrontDoorCdnEndpoint -AcquirePolicyToken, which already worked before this fix).Deeper verification — proof the token acquisition pipeline actually fired
Binding the parameter and getting a successful create doesn't, by itself, prove ARM ever received the policy token — the write could succeed either way on a subscription that doesn't mandate it. The AutoRest-generated cmdlet pipeline hook (
ContextAdapter.AddChangeSafetyPolicyTokenHandler, azure-powershell side) has no built-in-Debugobservability (unlike the SDK/RM cmdlet path), so a temporary, env-var-gated trace was added toPolicyTokenAcquirer.StampPolicyTokenAsync's call site (reverted afterward, verified via cleangit status) to get definitive proof:This confirms: the outgoing
PUTwas intercepted, a realPOSTto ARM'sacquirePolicyTokenendpoint was made and returned200 OK, the token was stamped onto the request as thex-ms-policy-external-evaluationsheader, and the subsequent async-pollGETwas correctly skipped by the write-verb gate — proving this generator fix results in the dynamic parameter both binding and driving real change-safety behavior, not just silently accepted.