What is the best way to implement a Whatif option into a script?

Welcome Forums General PowerShell Q&A What is the best way to implement a Whatif option into a script?

Viewing 8 reply threads
  • Author
    Posts
    • #191434
      Participant
      Topics: 3
      Replies: 11
      Points: 26
      Rank: Member

      I would like some suggestions on the best, most efficient, easiest, etc ways to implement a Whatif option into a script?

    • #191443
      Participant
      Topics: 1
      Replies: 1632
      Points: 3,074
      Helping Hand
      Rank: Community Hero

      It will be hard to recommend something meaningful without to know what your script is actually about. Did you know that all Powershell cmdlets potentially able to harm your computer have a parameter -WhatIf? You can read more about this with Get-Help about_common_Paramters.

      • #191452
        Participant
        Topics: 3
        Replies: 11
        Points: 26
        Rank: Member

        Thank you for replying. I'm using a series of functions to migrate Exchange 2010 public folders to Exchange online shared mailboxes. Function example below. I would like to have a whatif for the new-mailbox cmdlet. Or optimally when running the script that contains all the functions include a WhatIf that then applies to all the functions within. I was trying to avoid using an If statement and including 2 separate cmdlets for each one in each function.

        PF-Migration.ps1 contains the following function:

        Function Create-SharedMailbox($List)
        {
            Foreach($Item in $List)
            {    
                $SMTP = $Item.PrimarySmtpAddress.local + "-PF" + '@' + $Item.PrimarySmtpAddress.domain
                $Name = "$($Item.DisplayName)-PF"
                $Alias = "$($Item.Alias)-PF"
                New-Mailbox -Shared -Name $Name -DisplayName $Name -Alias $Alias -UserPrincipalName $SMTP  
            }
        
        } # Function Close
    • #191455
      Participant
      Topics: 1
      Replies: 1632
      Points: 3,074
      Helping Hand
      Rank: Community Hero

      Hmmm ... that's what I meant in my first answer. New-Mailbox has the support for -WhatIf built in – you just have to use it. Please read the help completely! 😉

      You may read as well the help for about_Functions_advenced_Parameters.

      • #191464
        Participant
        Topics: 3
        Replies: 11
        Points: 26
        Rank: Member

        Sorry, I may not be clear on what I'm looking for, and I'm a hack. I know the new-mailbox cmdlet has a WhatIf parameter built in. I've read read up on functions, although I'm not great with them beyond the basics, so I haven't bothered to go re-read the link. I will if you're suggesting it can help me figure out a good way to do this. I would like an admin that's running the script, or the function within the script itself to be able to use a whatIf. So if I run "Create-SharedMailbox $Name -whatif" , then the function will account for it by running new-mailbox with a WhatIf switch. If I run just "Create-SharedMailbox $Name" then it will run new-mailbox without the Whatif and create the mailbox.

    • #191470
      Participant
      Topics: 1
      Replies: 1632
      Points: 3,074
      Helping Hand
      Rank: Community Hero

      I got this. 😉 But you will need to implement this by yourself. There's no magic trick you can swing your magic wand to make this work. You will need to add a param block with a switch parameter and add a condition to the cmdlet you like.

      You should ALWAYS read the complete help including the examples for the cmdlets you're about to use to learn how to use them. You may consider using the common parameter -Confirm. With this the user gets a confirmation prompt and has to confirm the action. 😉

    • #191479
      Senior Moderator
      Topics: 8
      Replies: 1141
      Points: 3,927
      Helping Hand
      Rank: Community Hero

      You can implement WhatIf to your script/function using [CMDletBinding()]

      # new script
      [CmdletBinding(SupportShouldProcess=$True)]
      Param(
          [string]$Name
      )
      
      Stop-Process -ProcessName $Name
      
      # call the abovescript
      start-process cmd # a test process to run the script
      .\abovescript.ps1 -Name cmd # this should close the cmd process running
      
      start-process cmd # another test process to run the script
      .\abovescript.ps1 -Name cmd -WhatIf # magic happens here.
      

      Below article gives you more information on this.

      https://vexx32.github.io/2018/11/22/Implementing-ShouldProcess/

      • #191659
        Participant
        Topics: 3
        Replies: 11
        Points: 26
        Rank: Member

        As a side note, in your example you missed an "s" at the end of "Support" in "SupportShouldProcess". It should be "SupportsShouldProcess". It throws the following error when it's not correct, "Property 'SupportShouldProcess' cannot be found for type 'System.Management.Automation.CmdletBindingAttribute'".

        I tried cmdletbinding, and I get essentially the opposite of what I wanted. For instance, with the Set-PFforwards function below when running "Set-PFforwards -list $list2 -whatif", the forward is created (Whatiif not used), but the out-file does use the Whatif. The Set-MailPublicFolder cmdlet does have a built in Whatif. Maybe it's because I'm remoting to Exchange to, and pulling down the cmdlets? So, what I really want is for the Exchange cmdlets to get the Whatif and the out-file not to.

        'What if: Performing the operation "Output to File" on target "C:\PFs\Logs\PF-Migration_2019-12-04_11.36.49.log".'

        Function Set-PFforwards()
        {    
            [CmdletBinding(SupportsShouldProcess=$True)]
            param($List)
                
            Foreach($Item in $List)
            {        
                $EXOSMTP = $Item.PrimarySmtpAddress.local + "-PF" + '@' + $Item.PrimarySmtpAddress.domain 
        
                Set-MailPublicFolder $item.displayname -DeliverToMailboxAndForward $TRUE -ForwardingAddress $EXOSMTP
        
                "$(Get-Date -Format "yyyy-MM-dd HH:mm:ss") Set-MailPublicFolder $($item.displayname) -DeliverToMailboxAndForward $TRUE -ForwardingAddress $($EXOSMTP)" | out-file $LogPath -Append
            }
        
        } # Function Close
    • #191563
      Participant
      Topics: 2
      Replies: 510
      Points: 1,301
      Helping Hand
      Rank: Community Hero

      Please never implement a manual `-WhatIf` switch. Common parameters exist for a reason, and should be utilised. Users of a script or function should never have to guess at whether a `-WhatIf` parameter was implemented correctly. The presence of that parameter as a common parameter typically indicates the author knew what they were doing. A manually-added parameter will raise a lot of questions, and you really have no way of knowing if the implementation is in any way adequate.

      Also, a manual implementation will not correctly pass along `-WhatIf` values to called cmdlets, whereas use of the common parameter will cause it to be automatically affixed to cmdlets that support it already. If you call the main function with `-WhatIf` and the function calls `Remove-Item`, then the `-WhatIf` value will be passed along to `Remove-Item`.

      @kvprasoon's example is a great place to start.

    • #191740
      Participant
      Topics: 2
      Replies: 510
      Points: 1,301
      Helping Hand
      Rank: Community Hero

      @MattB depending on exactly what that command is, you may have trouble passing it along automatically. For clarity; -WhatIf options implemented via SupportsShouldProcess will be passed along to cmdlets (that is, compiled commands imported from a DLL module) and functions that belong to the same module as the command implementing SupportsShouldProcess. If you have a script command from a separate module that implements ShouldProcess, there is a long-standing bug that prevents the state being copied across.

      If you need to work with those commands, you can manually pass along the current -WhatIf value that is set by ShouldProcess by applying -WhatIf:$WhatIfPreference. That does assume that the command you're calling actually implements SupportsShouldProcess and is doing the proper checks. If it does not, you can instead use the examples in the blog post that @kvprasoon linked in order to check the current ShouldProcess state before calling the Set-MailPublicFolder command.

      And if you need to suppress the WhatIf state from a specific cmdlet call, you can apply -WhatIf:$false on that call.

    • #191758
      js
      Participant
      Topics: 27
      Replies: 716
      Points: 1,881
      Helping Hand
      Rank: Community Hero

      Here's a whatif/confirm example from Windows Powershell in Action:

      function Stop-ProcessUsingWMI
      {
        [CmdletBinding(SupportsShouldProcess=$True)]
        param(
          [parameter(mandatory=$true)] [regex] $pattern
        )
        foreach ($process in Get-WmiObject Win32_Process |
          where { $_.Name -match $pattern })
        {
          if ($PSCmdlet.ShouldProcess(
            "process $($process.Name) " +
             " (id: $($process.ProcessId))" ,
          "  Stop Process"))
          {
            $process.Terminate()
          }
        }
      }
      
      notepad
      
      Stop-ProcessUsingWMI notepad -Whatif
      
      What if: Performing operation "Stop Process" on Target
      "process notepad.exe (id: 6748)".
      
      Stop-ProcessUsingWMI notepad -Confirm
      
      Confirm
      Are you sure you want to perform this action?
      Performing operation "Stop Process" on Target
      "process notepad.exe (id: 6748)".
      [Y] Yes [A] Yes to All [N] No [L] No to All
       Suspend[?] Help (default is "Y"): y
      
    • #191842
      Participant
      Topics: 3
      Replies: 11
      Points: 26
      Rank: Member

      Thank you for your help!!! I'm under a time crunch with some things including having a final or workable product for the PF work, so I can't spend time on this right now. I plan to revisit next week.

Viewing 8 reply threads
  • You must be logged in to reply to this topic.