diff --git a/powershell/cmdlets/class.ts b/powershell/cmdlets/class.ts index 37a384a4681..1b190095cce 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,39 @@ export class CmdletClass extends Class { extensibleParametersProp.get = toExpression(`${extensibleParameters.value} `); this.add(extensibleParametersProp); } + private implementChangeSafetyParameters() { + // 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. + // 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; + } + // 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; + } + + 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: '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`] })); + } private implementIEventListener() { const $this = this; const cts = this.add(new Field('_cancellationTokenSource', System.Threading.CancellationTokenSource, { diff --git a/powershell/internal/project.ts b/powershell/internal/project.ts index 4d61fa74bf5..03c6b71565c 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');