How to get started writing a PowerShell script | From idea to action


When you’re given a task or you have a task you want to automate, what approach do you take? What kind of steps do you do? Let’s say you want to copy files from a directory to another.

The first thing I like to do is to see what kind of cmdlets that is available in regards of the task you want to solve. This can be done by running the following command:

# This outputs a list of all available commands
PS C:\>Get-Command

With this command you can filter on parameters like name, noun and verb to find the most suitable cmdlet to use. Since we are going to create a script that copies files, we can get all commands containing copy with the examples below. Note, that I might have different modules and cmdlets installed, therefore some cmdlet may be differerent. From the result below, we will use the cmdlet Copy-Item.

# Filtering using -Verb parameter
PS C:\>Get-Command -Verb Copy

# Outputs:
CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Function        Copy-NetFirewallRule                               2.0.0.0    NetSecurity
Function        Copy-NetIPsecMainModeCryptoSet                     2.0.0.0    NetSecurity
Function        Copy-NetIPsecMainModeRule                          2.0.0.0    NetSecurity
Function        Copy-NetIPsecPhase1AuthSet                         2.0.0.0    NetSecurity
Function        Copy-NetIPsecPhase2AuthSet                         2.0.0.0    NetSecurity
Function        Copy-NetIPsecQuickModeCryptoSet                    2.0.0.0    NetSecurity
Function        Copy-NetIPsecRule                                  2.0.0.0    NetSecurity
Cmdlet          Copy-Item                                          3.1.0.0    Microsoft.PowerShell.Management
Cmdlet          Copy-ItemProperty                                  3.1.0.0    Microsoft.PowerShell.Management
Cmdlet          Copy-VMFile                                        2.0.0.0    Hyper-V

# Filtering using -Name parameter, to find commands that contains the word Copy
Get-Command -Name "*Copy*"

# Output:
CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           copy -> Copy-Item
Function        Copy-NetFirewallRule                               2.0.0.0    NetSecurity
Function        Copy-NetIPsecMainModeCryptoSet                     2.0.0.0    NetSecurity
Function        Copy-NetIPsecMainModeRule                          2.0.0.0    NetSecurity
Function        Copy-NetIPsecPhase1AuthSet                         2.0.0.0    NetSecurity
Function        Copy-NetIPsecPhase2AuthSet                         2.0.0.0    NetSecurity
Function        Copy-NetIPsecQuickModeCryptoSet                    2.0.0.0    NetSecurity
Function        Copy-NetIPsecRule                                  2.0.0.0    NetSecurity
Cmdlet          Copy-GPO                                           1.0.0.0    GroupPolicy
Cmdlet          Copy-Item                                          3.1.0.0    Microsoft.PowerShell.Management
Cmdlet          Copy-ItemProperty                                  3.1.0.0    Microsoft.PowerShell.Management
Application     Robocopy.exe                                       10.0.14... C:\Windows\system32\Robocopy.ex
Application     xcopy.exe                                          10.0.14... C:\Windows\system32\xcopy.exe


# Examples of how to get different commands
# by specifying the verb parameter.

Get-Command -Verb Get
Get-Command -Verb Set
Get-Command -Verb Remove
Get-Command -Verb Add
Get-Command -Verb Update
Get-Command -Verb New
Get-Command -Verb Disable
Get-Command -Verb Enable
Get-Command -Verb Test

When we have found the proper cmdlet, we need to know how it can help us, by asking for help. This can be done by using the cmdlet Get-Help. This gives you a manual on how the cmdlet works. and there are many different ways you can ask for help, that is often useful when learning about a new cmdlet. See some examples below.

# This opens a separate window with all the help information
Get-Help Copy-Item -ShowWindow

# This opens a new browser tab with on Microsofts documentation page forthe cmdlet
Get-Help Copy-Item -Online

# Displays usage examples in the PowerShell console
Get-Help Copy-Item -Examples

# Display more in dept details about the cmdlet
Get-Help Copy-Item -Detail

# If you know a parameter to a cmdlet and need help for that specificparameter
# ask for that parameter spesifically with the following for instance
Get-Help Copy-Item -Parameter Destination

Now that we know a thing or two on how to get help, we can start by testing out the cmdlet in the command line. Lets say you want to take copy of your Documents folder to a Backup folder, we can do this by running the following command.

# Copies the CopyMe.txt file in C:\Documents to the C:\Backup folder
PS C:\> Copy-Item -Path C:\Documents\CopyMe.txt -Destination C:\Backup

# If you want to experiment with the same folders and file,
# you can run the commands below to create them.
PS C:\>New-Item -Path C:\ -Name Documents -ItemType Directory
PS C:\>New-Item -Path C:\ -Name Backup -ItemType Directory
PS C:\>New-Item -Path C:\Documents -Name CopyMe.txt -ItemType File

This command does the trick, but typing this command for each item in a folder manually isn’t very efficient. Now, let’s see how we can do this for each item within a folder now that we have the core command in place.

Looping through folders

To find how we can loop through each item in a folder we can use Get-Help foreach.

PS C:\>Get-Help foreach

The foreach lets us repeat a section of code, in this case repeating the Copy-Item cmdlet for each item in a specified directory, but first we need to now how to list out all the items in a folder. To find out, we can use the Get-Command again to look for something that lists items in a current directory.

# This will output every command that have the verb Get
PS C:\>Get-Command -Verb Get
# Look for the cmdlet "Get-ChildItem" this will list out all items i current directory

Now that we now how to list items in a directory and we know how to repeat a task for each item in a directory, we can now combine these two cmdlets with the following.

# One way to do it
foreach ($Item in Get-ChildItem -Name C:\Documents) {
Copy-Item -Path $Item -Destination C:\Backup\$Ite }

# Another way to do it, the easiest in my opinion and the one used as an example in rest of the post
Get-ChildItem -Path "C:\Documents" | foreach {
    Copy-Item -Path $_ -Destination "C:\Backup"
}

The first example code says “for each object in the parentheses, perform the action that’s defined within the curly braces. The Get-ChildItem in the foreach statement grabs a list of items in the specified path and stores them temporarily in the $Item variable. Inside the loop the $Item variable is used to refer to the single object returned by this particular iteration of the loop. For each iteration the Copy-Item command is called and the item is moved to the specified destination.

The second example pipes the result from the Get-ChildItem cmdlet to the foreach command and for each item copy the result to the specified destination. The $_ variable can be seen as the $Item variable in the first example, it refers to the single object returned by the particular iteration of the loop. This is one way we can repeat tasks for multiple items.

Adding variables

Furthermore, that we can copy all items in a folder, we can further enhance the script by adding variables. This makes it easier to edit what the parameters in the script, by separating the functionality and values, it’s also a step towards avoiding hard coded values. If you save and run the script below you will be able to copy items specified in the $Path variable to the path specified in the $Destination variable. By creating variables you can edit the value without editing the action of the command. Save the file as Copy-Items.ps1 and then run it from the command line and the script will execute the script (if you have the same file and folders) otherwise it will display an error message that the current path does’t exists. Execute the script by running for instance PS C:\>ScriptsCopy-Items.ps1.

$Path = "C:\Document"
$Destination = "C:\Backup"

Get-ChildItem -Path $Path | foreach {
    Copy-Item -Path $_ -Destination $Destination
}

Parameterizing the script

To enhance the script even more we can add parameters to the script, this allows us to specify the value of the variables, which let’s us specify the directory we want to copy and the destination we want to copy, each time we run the script. To execute the script below with parameters, you can do as follow: C:\ScriptsCopy-Item -Path C:\Documents -Destination C:\Backup. It allows us to specify the values of the variables more interactively than in the previous step, and is starting to become a more useful tool.

param(
    $Path,
    $Destination
)
Get-ChildItem -Path $Path | foreach {
    Copy-Item -Path $_ -Destination $Destination
}

To further improve the script we can explicit define the data type of the parameters. This helps validating that the input parameter has the correct data format.

param(
    [string]$Path,
    [string]$Destination
)
Get-ChildItem -Path $Path | foreach {
    Copy-Item -Path $_ -Destination $Destination
}

Creating a function

Further more, we can create a function out of the script. A PowerShell function is a list of statements that has a name assigned to it, in this case we call it Copy-Items, since we are copying all items in a specified folder. To call a function you need to load the script into memory by using dot source like this . C:\ScriptsCopy-Items.ps1, then you can run the function just like a PowerShell cmdlet, Copy-Items -Path C:\Documents -Destination C:\Backup.

Remember when you’re working with a function you must load it into memory each time you make an adjustment to it, otherwise you can be quite confused when you run the script and nothing different is happening. It can be useful to open a new PowerShell instance with or using the up arrow on the key board to reuse the dot source command, this is quite effective when you get use to it.

function Copy-Items {
    param(
        [string]$Path,
        [string]$Destination
    )
    Get-ChildItem -Path $Path | foreach {
        Write-Verbose "Copying item $_ to $Destination folder.."
        Copy-Item -Path $_ -Destination $Destination
        Write-Verbose "$_ copied to $Destin
    }
} #end Copy-Items function

Naming convention

When creating function and PowerShell tools, use the same naming convention as the PowerShell cmdlets. This makes it more intuitive and easy to use by keeping it consistent. You don’t need to invent the wheel again. For instance in the script we have created I’ve used the same variable names as the parameters to keep it consistent and intuitive, and easier for other to get started when there is one way to name variables and parameter names.

More, when creating a function use the Verb-Noun naming standard. To get a list of all available verbs in PowerShell, you can use the cmdlet Get-Verb. Use a verb that matches to action of the script and make sure to use descriptive names that explains the functionality of the script. Also use the PowerShell case standard, Pascal case.

In short the reason you want to follow the PowerShell naming convention is that you want to build your tools and functions to be exactly like a PowerShell command.

Creating a cmdlet

Adding [CmdletBindng()] to the script. When PowerShell see this attribute, it treats the command like a cmdlet. A cmdlet has a lot of neat features, one of them is the cmdlet Write-Verbose, this allows you to verify what the script is doing while running, by outputting a descriptive text you can activate when the function runs, by adding the -Verbose parameter, like so Copy-Items -Path C:\Documents -Destination -Verbose. The [CmdletBindng()] also a has other features like Debug, Write-Verbose, ErrorAction and more. In this post we focus on -Verbose.

function Copy-Items {
    [CmdletBinding()]
    param(
        [string]$Path,
        [string]$Destination
    )
    Get-ChildItem -Path $Path | foreach {
        Write-Verbose "Copying item $_ to $Destination folder.."
        Copy-Item -Path $_ -Destination $Destination
        Write-Verbose "$_ copied to $Destination"

    }
} #end Copy-Items function

When you run the function, this will output

PS C:\> Copy-Items -Path C:\Documents\CopyMe.txt -DestinationC:\Backup\ -Verbose
VERBOSE: Copying C:\Documents\CopyMe.txt to C:\Backup folder..
VERBOSE: C:\Documents\CopyMe.txt copied to C:\Backup
PS C:\>

A final touch

Now we have a full worthy script that let’s us copy files within a folder to another folder, the last thing we need to do is creating documentation of the script.

<#
.SYNOPSIS
    Copies items from one location to another.
.DESCRIPTION
    The Copy-Items cmdlet copies all itemsspecified in the Path variable to the pathspecified in the Destination parameter. Insimple terms it copies items from onelocation to another location.
.EXAMPLE
    PS C:\> Copy-Items -Path C:\Documents-Destination C:\Backup
    This example copies all items in theC:\Documents folder to the C:\Backup folder.
.PARAMETER Path
    Specifies the folder you want to copy itemsfrom.
.PARAMETER Destination
    Specifies the folder you want to copy itemsto.
#>
function Copy-Items {
    [CmdletBinding()]
    param(
        [string]$Path,
        [string]$Destination
    )
    Get-ChildItem -Path $Path | foreach {
        Write-Verbose "Copying item $_ to$Destination folder.."
        Copy-Item -Path $_ -Destination$Destination
        Write-Verbose "$_ copied to $Destination"
    }
} #end Copy-Items function

If you now run Get-Help Copy-Items, you can get the help information about the cmdlet, this is useful if you have created the script once, and then need to run in a later state, also if others is using the tool.

PS C:\> Get-Help Copy-Items

NAME
    Copy-Items

SYNOPSIS
    Copies items from one location to another.


SYNTAX
    Copy-Items [[-Path] <String>] [[-Destination] <String>] [<CommonParameters>]


DESCRIPTION
    The Copy-Items cmdlet copies all items specified in the Path variable to the path specified in the Destination para
    meter. In simple terms it copies items from one location to another location.


RELATED LINKS

REMARKS
    To see the examples, type: "get-help Copy-Items -examples".
    For more information, type: "get-help Copy-Items -detailed".
    For technical information, type: "get-help Copy-Items -full".

To wrap up

Now we have create a simple script using simple steps to create a fully working script. I hope you find the steps useful and understandable. I’ve also added a list below with some other tips that came to mind when writing this post. Hope you enjoyed and learned something from it.

Tips:

  • Don’t start creating a script, the first step is to test and figure out the core command in the console. Then when you’re troubleshooting you only have one problem to relate to, then take baby steps until you have a fully working tool, like we did in this post.
  • Use double quotations when using cmdlets like Write-Verbose, Write-Host and Write-Output, then you can use variables without have to concatenate.
  • If you us PowerShell ISE you can put your cursor on a cmdlet, and hit F1 and then a new window with the help manual for that cmdlet opens up.
  • Don’t use Write-Host if you plan to make modifications to the object you write to the console, this cmdlet will only output plain text, while Write-Output keeps the object in place, and you can make modification to the object. The Write-Host is useful when writing status updates when scripts are run by using it’s parameter -Foregroundcolor where you add colors, for instance; red for errors, green for success, yellow for warning.