Skip to content

Instantly share code, notes, and snippets.

@markcowl
Last active March 29, 2017 22:10
Show Gist options
  • Save markcowl/338e16fe5c8bbf195aff9f8af0db585d to your computer and use it in GitHub Desktop.
Save markcowl/338e16fe5c8bbf195aff9f8af0db585d to your computer and use it in GitHub Desktop.

Overview

We are making a significant change to the Azure PowerShell cmdlets in the next two releases to resolve a long-standing mismatch between recommended PowerShell practices for confirmation and the default usage of ShouldProcess and ShouldContinue methods and the accompanying Switch Parameter ‘Force’ in Azure PowerShell cmdlets.

The first part of this change, is modeled in this PR Azure/azure-powershell#2535 .

This PR implements confirmation correctly for all cmdlets that currently have a Force parameter. It does not introduce any breaking change for scripts, but it does mark any unnecessary Force parameters as obsolete. These parameters will be removed in phase 2 of this change, in the next (August) sprint. The main impact of this change is to allow PowerShell users to use standard PowerShell tools to discover and control cmdlet processing. The PR also introduces tools to detect violations of correct usage and fail the build if they occur – this will help ensure that all new PowerShell cmdlets will implement Confirmation correctly.

The second part of this change, coming next sprint, will implement ShouldProcess confirmation across the remainder of cmdlets that make changes or have side effects, and remove the obsoleted Force parameters

Since this PR introduces no breaking script changes, there is no further action required, but please feel free to review the PR and add comments. The PR commits are organized by feature team, so it should be easy to find any portion that affects your team’s cmdlets.

Details of the change, the impacts on code and scripts, and background are included below.

What is the Change?

Most Azure PowerShell cmdlets implement PowerShell ShouldProcess prompting using a pattern similar to the following:

If (Force || ShouldProcess(target, actionMessage)) {do cmdlet work
}

We are changing this implementation to be in line with PowerShell guidelines. For some cmdlets, this means removing the Force parameter, and changing the default implementation for ShouldProcess as follows:

If (ShouldProcess(target, actionMessage)) {do cmdlet work
}

By default, ShouldProcess interacts with the cmdlet ConfirmImpact and the user’s $ConfirmPreference setting to decide whether to prompt the user before continuing cmdlet processing. The $ConfirmPreference determines the level at which Confirmation automatically occurs, and no prompt is shown. If the ConfirmImpact specified in a cmdlet is at or below the level of $ConfirmPreference, then processing occurs automatically without displaying a prompt. Since both ConfirmImpact and $ConfirmPreference are set by default to ‘Medium’, this means that, by default, no confirmation prompt is shown and the cmdlet executes normally.
PowerShell has several tools that allow users to explore and control what happens during execution of cmdlets, and this change in implementation allows them to use these tools. Users who specify –Confirm on the command prompt will automatically be prompted for any call to ShouldProcess, and can decide whether to continue processing. Users can increase or decrease their $ConfirmPreference to decide which kinds of changes will be confirmed automatically without user interaction. Users can specify the –WhatIf parameter to see all the ShouldProcess prompts that would occur during execution of a cmdlet, without actually making any changes.

Some cmdlets require additional confirmation. For example, if a cmdlet would destroy existing resources in some circumstances, the cmdlet might detect that condition and prompt the user to verify before continuing. Overwriting an existing resource during resource creation, overwriting a file when downloading data, deleting a resource that is currently in use, or deleting a container that contains additional resources are all examples of this pattern. To implement additional confirmation, and allow scripts to opt out of additional prompts, the above pattern is enhanced with calls to ShouldContinue() and the Force parameter:

If (ShouldProcess(target, actionMessage)) {do processing and check whether additional prompting is required
  If (Force || ShouldContinue(shouldContinueWarning, shouldContinueCaption))
{
   … make change that required confirmation
}

Notice that to automatically skip Prompts for such a cmdlet requires the user to supply the Force parameter:

My-CmdletWithConfirmation –Force

Also note that, if you are unsure of the $ConfirmPreference setting in the current environment, you can skip both sets of prompts using:

My-CmdletWithConfirmation –Force –Confirm:$false

Additional Common Methods to Support the Change

To support this change, the following method overloads were added to AzurePSCmdlet. Partners may use these methods to properly implement confirmation in the implementation of ProcessRecord()

        /// <summary>
        /// Prompt for confirmation depending on the ConfirmImpact. By default No confirmation prompt 
        /// occurs unless ConfirmImpact > $ConfirmPreference.  Guarding the actions of a cmdlet with this 
        /// method will enable the cmdlet to support -WhatIf and -Confirm parameters.
        /// </summary>
        /// <param name="processMessage">The change being made to the resource</param>
        /// <param name="target">The resource that is being changed</param>
        /// <param name="action">The action to perform if confirmed</param>
        void ConfirmAction(string processMessage, string target, Action action);


        /// <summary>
        /// Guards execution of the given action using ShouldProcess and ShouldContinue.  The optional 
        /// useShouldContinue predicate determines whether ShouldContinue should be called for this 
        /// particular action (e.g. a resource is being overwritten). By default,  only ShouldProcess 
        /// will be executed unless useShouldContinue returns true.  Cmdlets that use this method overload 
        /// must have a force parameter. Guarding the actions of a cmdlet with this 
        /// method will enable the cmdlet to support -WhatIf and -Confirm parameters.
        /// </summary>
        /// <param name="force">Do not display a ShouldContinue prompt under any circumstances</param>
        /// <param name="continueMessage">A user prompt to confirm the destructive change if useShouldContinue returns true</param>
        /// <param name="processMessage">A description fo the normal action performed by the cmdlet.</param>
        /// <param name="target"> The resource that is being changed </param>
        /// <param name="action"> The action to perform if confirmed </param>
        /// <param name="useShouldContinue">A predicate indicating whether ShouldContinue should be invoked for this action</param>
        void ConfirmAction(bool force, string continueMessage, string processMessage, string target, Action action, Func<bool> useShouldContinue);

Why are we making this change?

Cmdlets that correctly implement PowerShell confirmation methods provide many useful features to knowledgeable PowerShell users, including:

  • Correct implementation of –WhatIf functionality, allowing a user to determine the effects of a cmdlet or script without making any actual changes
  • Control over prompting using a session-wide $ConfirmPreference, so that the user is prompted based on the impact of a prospective change (as reported in the ConfirmImpact setting in the cmdlet)
  • Cmdlet-specific control over confirmation prompts using the –Confirm parameter
  • Consistent use of ShouldContinue and the –Force parameter across cmdlets, for only those actions that would require prompting from the user due to the special nature of the changes (for example, deleting hidden files)
  • Consistency with other PowerShell cmdlets, so that PowerShell scripting knowledge from other cmdlets is immediately applicable to the Azure PowerShell cmdlets.

You can see additional details about best practices for PowerShell confirmation here: https://technet.microsoft.com/en-us/library/bb204629(v=VS.85).aspx Some sample customer feedback on this issue can be found here:

Azure/azure-powershell#475

Azure/azure-powershell#718

Azure/azure-powershell#827

Azure/azure-powershell#1255

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment