PowerShell Scripting Introductory Clinic
A introductory guide to PowerShell Scripting – PowerShell Version 7 is used in this clinic. Topics in this clinic are meant to expose you to concepts, they are not exhaustive in their scope. Questions: [email protected]
PowerShell Scripting Introductory Clinic
After you have studied this entire clinic, you should be able to completely understand a script such as:
Cimitra’s Windows Printer Control Script [ CLICK HERE ]
You will also be able to peruse a much more advanced script such as:
Cimitra’s Windows Users Administration Practice [ CLICK HERE ]
PowerShell Script Editing
You will want to create your PowerShell scripts in an editor that understands the PowerShell language. See this tutorial (CLICK HERE) if you need more guidance on this topic.
Get Information
On a Windows device, there are thousands of bits of information you can get programmatically. Open up a PowerShell 7 session, make sure to Run as administrator.
Get a listing of all of the services on your Windows computer:
Get-Process
[ A list of all the processes on your Windows computer comes back ]
Get information on the Spooler SubSystem App
Get-Process spoolsv
Extract Useful Data
When showing the Spooler SubSystem, there are handy bits of information that can be extracted. For example the Windows Process ID (PID) for the Spooler can be obtained in this manner.
(Get-Process spoolsv).Id
Get the Start Time for the Spooler process.
(Get-Process spoolsv).StartTime
Pipe the Get-Process cmdlet to the Get-Member cmdlet to discover all of the potential properties and methods available for the spoolsv object.
Get-Process spoolsv | Get-Member
PowerShell has a lot of shortcuts to popular commands which are referred to as an alias to the command. The Get-Member cmdlet has an alias of gm.
Get-Process spoolsv | gm
A command alias is meant to make working at a PowerShell command prompt easier. In scripts it is best to not use aliases, because it might be harder to understand the script when you come back to it.
Advanced Data Extraction
In order to extract useful data from something you need to capture it as an object. You name the object by using a dollar sign before the object name you would like to create. See below.
$<The Name You Want> = <The Information You Get From Windows>
$ThePrinter = Get-Process spoolsv
[ You just created an Object called $ThePrinter ]
The object $ThePrinter contains all the attributes of the Windows Process spoolsv at the time you issued the Get-Process command.
A couple of tips about creating objects
1. Start them with a letter
2. Make names that describe the object clearly
The Print Spooler object has dozens of “properties”. Let’s list them all by piping $ThePrinter to the Select-Object Cmdlet where we will ask for the properties of the object $ThePrinter.
$ThePrinter | Select-Object -Properties *
Now that you can see all the properties of an object, it is easy to get the properties by their name.
$ThePrinter.Id
$ThePrinter.StartTime
You can now capture single properties of an object into another object.
$TheCurrentPrinterPid = $ThePrinter.Id
$TheCurrentPrinterStartTime = $ThePrinter.StartTime
Section 1 Summary
So far you have been exposed to some new terminology and methods. Let’s review them
Cmdlet
Windows PowerShell has hundreds of Cmdlets . . . pronounced “Command Lets”. Here are the Cmdlets you have been exposed to so far in this clinic.
Get-Process
Get-Member
Select-Object
Cmdlet Alias
Some Cmdlets have an alias which is just a short named pointer to the Cmdlet.
Get-Member Alias-> gm
Select-Object Alias-> select
Cmdlet Naming Convention
Cmdlets are named with the convention: Verb-Noun
Said another way they are named using: Action-Object Name
Object Properties
All Windows Objects have “properties”. Properties allow you to get useful bits of information about an object.
Object Methods
You can take action by calling a “method” associated with an object. For example, let’s stop a the Spooler SubSystem App by doing the following.
$ThePrinter = Get-Process spoolsv
$ThePrinter.Kill()
To restart the Spooler SubSystem App do the following.
Restart-Service spooler
Pipe (Piping)
You can send output and objects from one Cmdlet to another with the Pipe symbol |
$ThePrinter | Select-Object -Properties *
Creating Your Own Object
You can create your own object in order to store the properties of other objects. Here are some examples:
$ThePrinter = Get-Process spoolsv
$TheCurrentPrinterPid = $ThePrinter.Id
Structure of a Script
A PowerShell script generally has the following components
- Comments
- A Description
- Parameters that get passed to the script
- Variables which store Parameters and other internal values
- Functions
Comments
Comments start with a pound symbol: #
Comments make your code more readable for the next script editor
– Remember, you are most likely the next editor of your script
You can add a comment right inside a line of code, example:
Stop-Service -Name ${SERVICE_NAME} #-Force
The above line will run Stop-Service without -Force
You can use any set of characters after a pound symbol
– Double pound symbol example: ## This is a FUNCTION ##
Description
A script Description isn’t required, but it is good practice
<#
.DESCRIPTION
This is the script DESCRIPTION
#>
The Description attribute of a PowerShell script is read by the PowerShell Get-Help Cmdlet
Example the command: Get-Help <The Script File> would produce
DESCRIPTION
This is the script DESCRIPTION
Parameters
Parameters are values that are passed into a script
Other than comments, Parameters must be at the very top of a PowerShell script.
Parameters are enclosed in the Param(…) structure
There are 2 common kinds of parameters
– Switches [switch]…
– String [string]…
Here is a simple example of the Param(…) structure
Param(
[switch] $Status,
[switch] $Restart
)
Parameters are used when a script is run in the following way:
ScriptName.ps1 -Restart
Variables
A typical PowerShell script has several variables
Variables are declared with a dollar sign at the front of a variable name, like this:
$TheNumberOne = 1
– OR –
$THE_NUMBER_ONE = 1
Make variable names descriptive
. . . Repeat
Make variable names descriptive
Making variable names descriptive is part of a bigger concept of making your code “Self Documenting”
By using descriptive variable names, sufficient comments, and descriptive function names your code will be a lot more readable.
Variable Scope
A Variable that is declared outside of a function is considered a global scoped variable
A Variable that is declared inside a function is considered a function scoped variable
The “scope” of a variable is an important concept. In a typical PowerShell script you use a lot of function scoped variables and a few global scoped variables.
Function Scoped Variable Example
function MyFunction(){
$TheMessage = “Hello World”
write-output $TheMessage
}
Global Scoped Variable Example
$Global:PROCESS_NAME = “spoolsv”
try{
$Global:ThePrinter = Get-Process $PROCESS_NAME -ErrorAction Stop
}catch{
$Global:PrinterRunning = $false
}
NOTE: The example above uses a “Try/Catch” condition that is very common in PowerShell scripts. You will learn more about Try/Catch.
Functions
If you have a very simple script, you may not use Functions. However if you have a more advanced script, you will use Functions.
– Keep functions short
– Have a function do one thing, and do it well
– Functions can have parameters passed to them
– Functions are like a mini-program
A Function is declared using the following structure
function FunctionName(){
# Function content
}
Here is an example function
function StartPrinter(){
Restart-Service -Name spooler -Force -ErrorAction Stop
}
Example Script
Structure of a Script
A PowerShell script generally has the following components
- Comments
- A Description
- Parameters that get passed to the script
- Variables which store Parameters and other internal values
- Functions
Here is a simple PowerShell script with these components
# Control a Windows Spooler SubSystem App
# Author: Tay Kratzer [email protected]
# Modify Date: 9/16/21
<#
.DESCRIPTION
Control The Windows Spooler SubSystem
#>
Param(
[switch] $Status,
[switch] $Restart
)
$Global:SERVICE_NAME = “spooler”
$Global:PROCESS_NAME = “spoolsv”
$Global:PrinterRunning = $true
try{
$Global:ThePrinter = Get-Process $PROCESS_NAME -ErrorAction Stop
}catch{
$Global:PrinterRunning = $false
}
function RestartPrinter(){
## If the service is running, stop the service ##
if($PrinterRunning){
Stop-Service -Name ${SERVICE_NAME} -Force
}
## Restart the Service ##
Restart-Service -Name ${SERVICE_NAME} -Force -ErrorAction Stop
}
function GetPrinterStatus(){
if($PrinterRunning){
$ThePrinterPID = $ThePrinter.Id
write-output “The Printer is Running”
write-output “The Printer Process ID is: $ThePrinterPID”
}else{
write-output “The Printer is Not Running”
}
}
if($Restart){
RestartPrinter
}
if($Status){
GetPrinterStatus
}
Section 2 Summary
A PowerShell script generally has the following components
- Comments
- A Description
- Parameters that get passed to the script
- Variables which store Parameters and other internal values
- Functions
Comments
Comments are meant to make a script more readable. Comments start with a pound symbol: # This is a comment
Description
The Description field is towards the top of the script, and is meant to be read by the Get-Help command.
Parameters
Parameters are used to pass in values or directives to a PowerShell script. Parameters must be at the very top of a script. Parameters are defined with the Param (…) structure. The Param structure is an Array and should have each parameter separated with a comma, and the last parameter does not need a comma.
Param(
[switch] $Status,
[switch] $Restart
)
Variables
Variables are meant to store values. A Variable starts with a dollar sign, such as $Status
Functions
An advanced PowerShell script typically has Functions. Functions are somewhat like a program in and of themselves. Functions use the function
keyword along with a name and the function is contained in French braces. Like this:
function TheFunctionOfAllFunctions(){
write-output “Hello World”
}
Conditionals
In a sentence, conditionals test for a state. Here are some conditionals
True/False
Greater Than
Equal To
Not Equal To
True/False
PowerShell has boolean conditional/variable types of True and False
One way these variables are used is
$MyAVariable = $true
$MyBVariable = $false
Greater Than
Another way to evaluate a condition as a boolean is as follows
$MyCVariable = “test”
($MyCVariable.Length -gt 0)
The parenthesis ( ) really means test this condition for it’s truthfulness.
($MyCVariable.Length -gt 0)
Is asking PowerShell. . .
Test for $MyCVariable.Length is greater than 0 ?
The result will be True
Equal To
$MyDVariable = 10
($MyDVariable -eq 20)
Test for $MyDVariable is equal to 20 ?
The result will be False
Not Equal To
$MyDVariable = 10
($MyDVariable -ne 20)
Test for $MyDVariable is not equal to 20 ?
The result will be True
If
The If method is combined with parenthesis () to make a very simple test method.
$TempDir = “c:\temp”
$FileCount = (Get-ChildItem $TempDir | Measure-Object).Count
if($FileCount -gt 100) {
Write-Output “Time to Clean Up The Temporary Files in: $TempDir”
}
If/Else
The Else statement extends the If method to perform additional actions.
if($FileCount -gt 100) {
Write-Output “Time to Clean Up The Temporary Files in: $TempDir”
}else{
Write-Output “There are $FileCount Files in: $TempDir”
}
Try/Catch
Try/Catch is a conditional similar to True/False
A Try/Catch tells PowerShell “try this, but if that fails then the catch event should be triggered”.
Global:PrinterRunning = $true
Try{
$Global:ThePrinter = Get-Process spoolsv -ErrorAction stop
}catch{
$Global:PrinterRunning = $false
}
The Pipeline Variable
PowerShell has a special variable that doesn’t need to be created by you. It is automatically created under certain circumstances. It is officially called the “Pipeline variable” It is accessible through: $PSItem -or- $_
The Pipeline variable is the “Current” value in a routine. It’s easiest to explain the Pipeline variable with an example.
# Create an array of items
$MyArray = (“Item1”, “Item2”, “Item3”)
# Use the ForEach method to list all elements in the array
$MyArray.ForEach({write-output “$PSItem”})
Item1
Item2
Item3
# Use ForEach again but use the Pipeline variable short syntax “$_”
$MyArray.ForEach({write-output “$_”})
Item1
Item2
Item3
# Use a Try/Catch, output the error which catch usually masks
Try{
# Try adding a number to a string, this will fail!
5 + “test”
}catch{
$TheError = $_
Write-Output “That did not work”
Write-Output “Here is the Error: $TheError”
}
The Pipeline variable is used quite a bit. Here is a more real-world example of a Pipeline variable.
# $MyGroup variable is assigned to the results of the Get-AdGroup command
$MyGroup= Get-AdGroup -Identity “CN=Admin Notify,OU=USER GROUPS,OU=GROUPS,OU=KCC,OU=DEMOSYSTEM,DC=cimitrademo,DC=com” -Properties Member
# Iterate through the “Member” attribute of $MyGroup and display full names only
$MyGroup.Member.ForEach({
$ThePerson = $_
$TheFullName = $ThePerson -replace “(CN=)(.*?),.*”,’$2′
Write-Output “$TheFullName”
})
Section 3 Summary
Conditionals
Try/False (boolean)
Less Than
Equal To
Greater Than
Conditionals are generally tested between parenthesis ( . . . )
Try/Catch
The Try/Catch method is a lot like a conditional. There are 2 states a Try/Catch statement can cause, which makes it similar to a True/False method.
Pipeline Variable
The Pipeline variable is an automatic variable that is created by PowerShell.
The Pipeline variable is accessible as $PSItem -or- $_
In PowerShell the Pipeline variable is assigned to the most current item or value in a routine. The Pipeline variable is often used when iterating through a list of items.
Object Types
There are several object types. Here are the ones we have used in this clinic:
Numbers
$TheNumber = 5
Number variables are meant to be used for math functions. A number is a numeric by default. The way to make a number not numeric is to include it in a string such as: $AString = “5”
Strings
$TheString = “Hello World”
String variables contain text. If you create a string, it should be delimited with double or single quotes. A lot of output you get from objects are strings.
You can join/concatenate strings in the following fashion:
$AString = “5”
$BString = “Tom”
$TheSentence = ($BString + ” is ” +$AString + ” years old.”)
Arrays
Arrays are lists of objects. Arrays are used very often. The Param structure of a PowerShell script is an array of Parameters that the script will accept with a -<Parameter> switch.
When you read objects, you will often have an array of items associated with that object. Reading through an Array object is often done through the <Array>.ForEach method.
Script Analysis
If you have studied this entire clinic, you should be able to completely analyze a script such as Cimitra’s Windows Printer Control Script [ CLICK HERE ]
A significantly more advanced script is Cimitra’s Windows Users Administration Practice [ CLICK HERE ]