PowerShell: Prevent Accidental Runs of Scripts

The problem and it’s a big one!

You might want to read this to the end! You are into PowerShell and have a ton of scripts. The problem is some of them run things that are not meant to be run at any time by anyone except you when you first set it up 🙂 – like dropping the entire contents of a folder and sub-folders to reinitialize.

Once you realize that you accidentally hit F5 on your “DropFolderContents.ps1” script, it is already too late. The damage is done.

So, how to stop accidental runs?

Here are some things you should do first before you deploy your scripts for common use

1) If this will stay a script, “Parameterize”.

In this example, I show how to create a script with parameters. Basically, if you have a script like this

$DatabaseSMO = Get-DbaDatabase -SqlInstance 'MySqlInstance' -Database 'MyDatabase';

#---------------------
#Drop all the stored procedures
#---------------------
 
$spsArray = $DatabaseSMO.StoredProcedures |
                Select-Object Schema,Name |
                Where-Object {($_.Schema -ne 'sys')};
foreach($sp in $spsArray)
{
    Write-Host("About to drop sp $($sp.Schema).$($sp.name)");
    Invoke-DbaQuery -SqlInstance $SqlInstance -Database $Database -Query "DROP PROCEDURE [$($sp.Schema)].[$($sp.name)]";
}

When someone runs this using F5 in the ISE, it will proceed to drop all the stored procedures within the database MySQLInstance.MyDatabase. This is BAD! REALLY BAD!

To prevent such an accidental run, you just “Parameterize” your script

#Usage 1:
<#
. c:\path\to\this\script\Remove-SQLDatabaseSPs.ps1 -SqlInstance 'MyServer\MyInstance' -Database 'MyDatabase'
#> 
param(
        [Parameter(Mandatory=$true)]
        [System.String]
        $SqlInstance,
 
        [Parameter(Mandatory=$true)]
        [System.String]
        $Database
    )

$DatabaseSMO = Get-DbaDatabase -SqlInstance $SqlInstance -Database $Database;
  
..Rest of the script is the same as the previous code example body!

Basically, we added a “param()” block at the top and an example to show how to use this script. Notice that the hard-coded SQLInstance and Database values from the last line above have been converted to variables – Parameter variables to be exact.

When then run the Parameterized version, since the two parameters are “mandatory”, PowerShell will prompt the user for the values and will only proceed after valid values are supplied.

2) Turn it into a Function!

The process of turning our script into a function is also quite simple and similar.

#Usage 1:
#Remove-SQLDatabaseSPs -SqlInstance 'MyServer\MyInstance' -Database 'MyDatabase'


function Remove-SQLDatabaseSPs
{
    [CmdletBinding()]
    param( 
        [Parameter(Mandatory=$true)]
        [System.String]
        $SqlInstance,
 
        [Parameter(Mandatory=$true)]
        [System.String]
        $Database
    )

    $DatabaseSMO = Get-DbaDatabase -SqlInstance $SqlInstance -Database $Database;
  
    ..Rest of the script is the same as the previous code example body!

}  #This function close curly brace was added at the end.

The concept and the benefit is the same when turning into a function but also, we have the added benefit of making it a part of a module which will then have this function among other functions.

3) Lazy? Just “Paste” this at the beginning and be done in 2 seconds!

#------------------------------------------
"PAUSED to prevent accidental kickoff's. CTRL+C to STOP now. ENTER to procced"
#------------------------------------------
PAUSE
"CTRL+C to STOP now. ENTER again to procced !"
PAUSE

With this if you have a ton of Hard-coding values inside your script, you don’t have to convert all of the hard-coded values into variables but you can just add this to the beginning and be done.

Get custom input too

It will stop everyone from proceeding unless they hit ENTER twice! You could even customize it to make it funny..

write-host -foregroundcolor yellow 'You hit F5. This will run this whole script.'
$response = Read-Host -Prompt 'Did you really mean to run the entire script (y/n)?' 

if ($response -eq 'y') {
    write-host 'You answered "yes" to proceed with running the script!'
    throw [System.ArgumentException] "Aborting 'Run as script'! This sample file is not intended to be run as a script. Please highlight and run selection with F8."
} elseif ($response -eq 'n') {
    write-host 'You answered no!'
    throw [System.ArgumentException] "Aborting 'Run as script'! Please highlight and run selection with F8."
} else {
    write-host 'You provided an incorrect response! Only y or n are acceptable!'
    throw [System.ArgumentException] "Expected y/n. Defaulting to 'n' and aborting. Please highlight and run selection with F8."
}

Save yourself!

No one posts online about the accidental damage (of some serious kind) they did by running a script they were not supposed to. I am sure they would have killed to add 2 lines of code (that I showed you) to the beginning of their script and saved their jobs!

One thought on “PowerShell: Prevent Accidental Runs of Scripts

  1. Thank you very much.
    I’ve added
    #——————————————
    “PAUSED to prevent accidental kickoff’s. CTRL+C to STOP now. ENTER to procced”
    #——————————————
    PAUSE
    “CTRL+C to STOP now. ENTER again to procced !”
    PAUSE

    I’m not sure I need the double pause, but just to be safe….

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s