From dfa19a3e310c2ccce61031f602fdfe4c035c2ced Mon Sep 17 00:00:00 2001 From: Yang An Date: Thu, 16 Jul 2026 17:27:57 +1000 Subject: [PATCH 1/4] Prototype: codegen-owned static Change Safety parameters (alternative to VTable dynamic-parameter delegate) Emit -AcquirePolicyToken (switch) and -ChangeReference (string) as static [Parameter] properties on azure write-verb cmdlets, categorized Azure. No IDynamicParameters, no Module.cs/VTable delegate wiring; the module-level HTTP pipeline step reads the values from BoundParameters by name. --- powershell/cmdlets/class.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index 37a384a468..b41eb88de2 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -460,6 +460,9 @@ export class CmdletClass extends Class { // implement part of the IContext this.implementIContext(); + // Change Safety: emit codegen-owned static parameters on write-verb cmdlets + this.implementChangeSafetyParameters(); + // add constructors this.implementConstructors(); @@ -1622,6 +1625,28 @@ export class CmdletClass extends Class { extensibleParametersProp.get = toExpression(`${extensibleParameters.value} `); this.add(extensibleParametersProp); } + private implementChangeSafetyParameters() { + // Change Safety (codegen-owned static parameters): only azure write-verb cmdlets + // (PUT/POST/DELETE/PATCH) expose the opt-in Change Safety parameters. The parameter + // values are read by the module-level HTTP pipeline step (in Az.Accounts) from the + // cmdlet's BoundParameters, so no runtime-wired delegate is required here. + if (!this.state.project.azure) { + return; + } + const httpMethod = (this.apiCall.requests?.[0]?.protocol.http?.method ?? '').toLowerCase(); + if (!['put', 'post', 'delete', 'patch'].includes(httpMethod)) { + return; + } + + const acquirePolicyToken = this.add(new Property('AcquirePolicyToken', SwitchParameter, { attributes: [], description: 'Acquire a policy evaluation token for this change and include it on the write request.' })); + acquirePolicyToken.add(new Attribute(ParameterAttribute, { parameters: ['Mandatory = false', 'HelpMessage = "Acquire a policy evaluation token for this change and include it on the write request."'] })); + acquirePolicyToken.add(new Attribute(CategoryAttribute, { parameters: [`${ParameterCategory}.Azure`] })); + + const changeReference = this.add(new Property('ChangeReference', dotnet.String, { attributes: [], description: 'A caller-supplied reference that correlates this change with an external change-management record.' })); + changeReference.add(new Attribute(ParameterAttribute, { parameters: ['Mandatory = false', 'HelpMessage = "A caller-supplied reference that correlates this change with an external change-management record."'] })); + changeReference.add(new Attribute(ValidateNotNull)); + changeReference.add(new Attribute(CategoryAttribute, { parameters: [`${ParameterCategory}.Azure`] })); + } private implementIEventListener() { const $this = this; const cts = this.add(new Field('_cancellationTokenSource', System.Threading.CancellationTokenSource, { From f5b1c782b709d2d667070c8f4b5201c7e7a80847 Mon Sep 17 00:00:00 2001 From: Yang An Date: Fri, 17 Jul 2026 09:57:38 +1000 Subject: [PATCH 2/4] Prototype: exclude data-plane cmdlets from Change Safety static parameters Gate implementChangeSafetyParameters on !endpointResourceIdKeyName (the generator's canonical data-plane signal, matching isDataPlane in module-class.ts), so only management-plane (ARM) write-verb cmdlets get -AcquirePolicyToken/-ChangeReference. --- powershell/cmdlets/class.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index b41eb88de2..b59ce56ac8 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -1626,13 +1626,19 @@ export class CmdletClass extends Class { this.add(extensibleParametersProp); } private implementChangeSafetyParameters() { - // Change Safety (codegen-owned static parameters): only azure write-verb cmdlets - // (PUT/POST/DELETE/PATCH) expose the opt-in Change Safety parameters. The parameter - // values are read by the module-level HTTP pipeline step (in Az.Accounts) from the - // cmdlet's BoundParameters, so no runtime-wired delegate is required here. + // Change Safety (codegen-owned static parameters): only azure management-plane (ARM) + // write-verb cmdlets (PUT/POST/DELETE/PATCH) expose the opt-in Change Safety parameters. + // The parameter values are read by the module-level HTTP pipeline step (in Az.Accounts) + // from the cmdlet's BoundParameters, so no runtime-wired delegate is required here. if (!this.state.project.azure) { return; } + // Change Safety is a management-plane concept; skip data-plane modules, which target a + // custom service endpoint (identified by an endpoint-resource-id-key-name). This matches + // the data-plane detection used in module-class.ts (isDataPlane). + if (this.state.project.endpointResourceIdKeyName) { + return; + } const httpMethod = (this.apiCall.requests?.[0]?.protocol.http?.method ?? '').toLowerCase(); if (!['put', 'post', 'delete', 'patch'].includes(httpMethod)) { return; From fa81926e6f2d0d330596a11ee735b9a6d78f4b23 Mon Sep 17 00:00:00 2001 From: Yang An Date: Fri, 17 Jul 2026 10:32:35 +1000 Subject: [PATCH 3/4] Align Change Safety static param help text with SDK (ChangeSafetyParameters) Use the exact parameter names and help messages defined in azure-powershell-common ChangeSafetyParameters so AutoRest and SDK cmdlets present identically to users. --- powershell/cmdlets/class.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index b59ce56ac8..ef7852b685 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -1644,12 +1644,12 @@ export class CmdletClass extends Class { return; } - const acquirePolicyToken = this.add(new Property('AcquirePolicyToken', SwitchParameter, { attributes: [], description: 'Acquire a policy evaluation token for this change and include it on the write request.' })); - acquirePolicyToken.add(new Attribute(ParameterAttribute, { parameters: ['Mandatory = false', 'HelpMessage = "Acquire a policy evaluation token for this change and include it on the write request."'] })); + const acquirePolicyToken = this.add(new Property('AcquirePolicyToken', SwitchParameter, { attributes: [], description: 'Acquire an Azure Policy token automatically for this resource operation.' })); + acquirePolicyToken.add(new Attribute(ParameterAttribute, { parameters: ['Mandatory = false', 'HelpMessage = "Acquire an Azure Policy token automatically for this resource operation."'] })); acquirePolicyToken.add(new Attribute(CategoryAttribute, { parameters: [`${ParameterCategory}.Azure`] })); - const changeReference = this.add(new Property('ChangeReference', dotnet.String, { attributes: [], description: 'A caller-supplied reference that correlates this change with an external change-management record.' })); - changeReference.add(new Attribute(ParameterAttribute, { parameters: ['Mandatory = false', 'HelpMessage = "A caller-supplied reference that correlates this change with an external change-management record."'] })); + const changeReference = this.add(new Property('ChangeReference', dotnet.String, { attributes: [], description: 'The change reference resource ID for this resource operation.' })); + changeReference.add(new Attribute(ParameterAttribute, { parameters: ['Mandatory = false', 'HelpMessage = "The change reference resource ID for this resource operation."'] })); changeReference.add(new Attribute(ValidateNotNull)); changeReference.add(new Attribute(CategoryAttribute, { parameters: [`${ParameterCategory}.Azure`] })); } From ac04dce92af9a2616c819f221040ff4ffad49255 Mon Sep 17 00:00:00 2001 From: Yang An Date: Fri, 17 Jul 2026 13:11:30 +1000 Subject: [PATCH 4/4] Make Change Safety static parameters opt-in via change-safety config flag Add a change-safety project flag (default false) read from the module readme. implementChangeSafetyParameters only emits -AcquirePolicyToken/-ChangeReference when it is true, so the ~180-module rollout is deliberate (a module opts in alongside bumping its Az.Accounts minimum) rather than triggered by any unrelated regeneration. --- powershell/cmdlets/class.ts | 5 +++++ powershell/internal/project.ts | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index ef7852b685..1b190095cc 100644 --- a/powershell/cmdlets/class.ts +++ b/powershell/cmdlets/class.ts @@ -1630,6 +1630,11 @@ export class CmdletClass extends Class { // write-verb cmdlets (PUT/POST/DELETE/PATCH) expose the opt-in Change Safety parameters. // The parameter values are read by the module-level HTTP pipeline step (in Az.Accounts) // from the cmdlet's BoundParameters, so no runtime-wired delegate is required here. + // Opt-in per module via `change-safety: true` in the module readme (default off), so the + // 180+ module rollout is deliberate rather than triggered by any unrelated regeneration. + if (!this.state.project.changeSafety) { + return; + } if (!this.state.project.azure) { return; } diff --git a/powershell/internal/project.ts b/powershell/internal/project.ts index 4d61fa74bf..03c6b71565 100644 --- a/powershell/internal/project.ts +++ b/powershell/internal/project.ts @@ -128,6 +128,7 @@ export class Project extends codeDomProject { public modelCmdletFolder!: string; public endpointResourceIdKeyName!: string; public endpointSuffixKeyName!: string; + public changeSafety!: boolean; public customFolder!: string; public utilsFolder!: string; @@ -334,6 +335,11 @@ export class Project extends codeDomProject { // configuration for whether to use fixed array in generated code of model, default is false this.fixedArray = await this.state.getValue('fixed-array', false); + // Change Safety: opt-in per module. When true, azure management-plane write-verb cmdlets get the + // -AcquirePolicyToken / -ChangeReference parameters. Default false so the rollout is controlled + // (a module opts in, alongside bumping its Az.Accounts minimum), not organic on every regeneration. + this.changeSafety = await this.state.getValue('change-safety', false); + // File paths this.csproj = await this.state.getValue('csproj'); this.dll = await this.state.getValue('dll');