PowerShell Scripts

This entry is part 11 of 13 in the series Learn PowerShell

PowerShell Scripts

Episodes 0-9 of the Learn PowerShell series covered the fundamentals of PowerShell. Now, in Episode 10, you have the basic elements to begin creating your own PowerShell scripts!

A PowerShell script is simply a ps1 file that contains PowerShell logic. What logic is entirely up to you and will be shaped by the purpose of the script. If you haven’t already you should begin attempting to write your first script. Not sure what to create? Think about a process that you perform often at work that requires a lot of manual steps. Try performing those same steps and save it as a script! If your first attempt turns out to be too difficult, drop down to a task that has lower complexity.

Every time you succeed, no matter how small, you’ll be freeing up your bandwidth! With that extra time in your day, you can write a little more PowerShell, which will create even more bandwidth to tackle more complex tasks. Before you know it, you’ll have a collection of scripts capable of performing a variety of tasks that used to be manual processes!

Video

If you prefer video format over written documentation, I discuss this topic in the following TechThoughts video:

Creating and Saving PowerShell Scripts

Creating a PowerShell script is as simple as saving your PowerShell commands with the .ps1 file extension. Go ahead and copy the simple code below and save it as math.ps1. We’ll use this simple script example moving forward.

$total = 2 + 2
$output = "two plus two is equal to $total"
Write-Output $output

Running PowerShell Scripts

You can run a PowerShell script by specifying the path to the script:

#windows
C:\scripts\math.ps1
#linux
/scripts/math.ps1

PowerShell Execution Policy

Depending on the configuration of your device and environment you may not be able to run PowerShell scripts. You may need to adjust your PowerShell Execution policy.

  • Restricted — Stops any script from running.
  • RemoteSigned — Runs scripts created on the device. However, scripts created on another computer won’t run unless they include a signature of a trusted publisher.
  • AllSigned — All the scripts will run as long as they’ve been signed by a trusted publisher.
  • Unrestricted — Runs any script without any restrictions.

To determine the current execution policy run the following cmdlet:

#get the execution policy of all scopes in the order of precedence
Get-ExecutionPolicy -List

To change the execution policy:

#change execution policy
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

Script Scope

When you run a script by specifying the ps1 file path, that script does not run in the scope of the current session. This means that if you open a PowerShell session, and run a script, you will not have access to the variables and information in your current session. You can see this in action for yourself. Open a PowerShell window and run the math.ps1 script from above.

After it runs, try typing $total in your PowerShell session. It will not return anything. This is because math.ps1 was run under the scope of a different PowerShell session. Once the script completed, so did that session.

If you need to access the information in the context of the current PowerShell session, you can dot source the PowerShell script. This is achieved by placing a dot and then a space before the script path.

# dot source a full file path
. C:\scripts\math.ps1
# dot source a local file
. .\math.ps1

PowerShell Script Example

If you’ve completed Episodes 0-9, you’re ready to start writing PowerShell scripts. You may not feel ready, but the only way to shake that feeling is to write more PowerShell. We’re going to kick things off with our first script. Before sitting down and writing code, you need to have an objective. Lets declare our script’s objective now.

We want to be notified when a drive in our home network is running low on space. The script will check the desired drive for free space, and send a Telegram notification if it is below 20%. All actions should be logged. The script should support scanning multiple different drives. The script should support both Linux and Windows.

If this is your first time writing a script, you may have no idea where to start. That’s OK. Just break the objective into manageable chunks:

  • Supports logging
  • Checks desired drive for free space
  • Sends Telegram notification
  • Lets user specify drive
  • Supports both windows and Linux

Try tackling these one at a time. In the video above you can watch me live-code this solution. This gives you a chance to see my thought process and workflow for tackling something like this. Alternatively, you can reference the solution below, and try scripting your own objective.

As an additional exercise try enhancing the script below to:

  • Permit the user to specify the percentage threshold
    • Tip: it’s currently hard-coded to 20
  • Not a Telegram user? Try adjusting the script to send an email instead.
param (
    [Parameter(Mandatory = $true)]
    [string]
    $Drive
)

if ($PSVersionTable.Platform -eq 'Unix') {
    $logPath = '/tmp'
}
else {
    $logPath = 'C:\Logs' #log path location
}

#need linux path

$logFile = "$logPath\driveCheck.log" #log file

#verify if log directory path is present. if not, create it.
try {
    if (-not (Test-Path -Path $logPath -ErrorAction Stop )) {
        # Output directory not found. Creating...
        New-Item -ItemType Directory -Path $logPath -ErrorAction Stop | Out-Null
        New-Item -ItemType File -Path $logFile -ErrorAction Stop | Out-Null
    }
}
catch {
    throw
}

Add-Content -Path $logFile -Value "[INFO] Running $PSCommandPath"

#verify that the required Telegram module is installed.
if (-not (Get-Module -ListAvailable -Name PoshGram)) {
    Add-Content -Path $logFile -Value '[INFO] PoshGram not installed.'
    throw
}
else {
    Add-Content -Path $logFile -Value '[INFO] PoshGram module verified.'
}

#get hard drive volume information and free space
try {
    if ($PSVersionTable.Platform -eq 'Unix') {
        $volume = Get-PSDrive -Name $Drive -ErrorAction Stop
        #verify volume actually exists
        if ($volume) {
            $total = $volume.Free + $volume.Used
            $percentFree = [int](($volume.Free / $total) * 100)
            Add-Content -Path $logFile -Value "[INFO] Percent Free: $percentFree%"
        }
        else {
            Add-Content -Path $logFile -Value "[ERROR] $Drive was not found."
            throw
        }
    }
    else {
        $volume = Get-Volume -ErrorAction Stop | Where-Object { $_.DriveLetter -eq $Drive }
        #verify volume actually exists
        if ($volume) {
            $total = $volume.Size
            $percentFree = [int](($volume.SizeRemaining / $total) * 100)
            Add-Content -Path $logFile -Value "[INFO] Percent Free: $percentFree%"
        }
        else {
            Add-Content -Path $logFile -Value "[ERROR] $Drive was not found."
            throw
        }
    }
}
catch {
    Add-Content -Path $logFile -Value '[ERROR] Unable to retrieve volume information:'
    Add-Content -Path $logFile -Value $_
    throw
}

#evaluate if a message needs to be sent if the drive is below 20GB freespace
if ($percentFree -le 20) {

    try {
        Import-Module PoshGram -ErrorAction Stop
        Add-Content -Path $logFile -Value '[INFO] PoshGram imported successfully.'
    }
    catch {
        Add-Content -Path $logFile -Value '[ERROR] PoshGram could not be imported:'
        Add-Content -Path $logFile -Value $_
        throw
    }

    Add-Content -Path $logFile -Value '[INFO] Sending Telegram notification'

    $messageSplat = @{
        BotToken    = "#########:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
        ChatID      = "-#########"
        Message     = "[LOW SPACE] Drive at: $percentFree%"
        ErrorAction = 'Stop'
    }

    try {
        Send-TelegramTextMessage @messageSplat
        Add-Content -Path $logFile -Value '[INFO] Message sent successfully'
    }
    catch {
        Add-Content -Path $logFile -Value '[ERROR] Error encountered sending message:'
        Add-Content -Path $logFile -Value $_
        throw
    }

}

Additional Reading

Series Navigation<< PowerShell RemotingPowerShell Functions >>

1 Response

  1. Chris Meunier says:

    Just wanted to say that your video series was waaaay easier to follow and understand than any other I tried to watch. Even those that I paid for. Thank you very much!

Leave a Reply

Your email address will not be published. Required fields are marked *