PowerShell Core, as the open source alternative to Windows PowerShell from which it evolved, is quickly becoming the automation engine of choice for many administrators and developers alike. As a true cross-platform shell, PowerShell Core is perfectly suited for many different operating system types and workloads.
While the next iteration of PowerShell Core at the time of writing was slated for May 2019 and will drop the Core this book and its recipes will remain accurate and useful. The release of PowerShell is merely a rebranding to unify PowerShell development. In the future we will continue to see rapid new iterations of PowerShell.
While installing PowerShell Core is a breeze, the first steps for people new to PowerShell can be quite challenging. In this chapter, I aim to bridge those little knowledge gaps and show you the ropes.
You'll learn all there is to know about installing and operating PowerShell Core and learn about the cmdlets necessary to find your way on any system running PowerShell Core. In addition to that, you'll discover how to get help without using the internet and do everything from within PowerShell.
This chapter is intended for beginners who are fairly new to PowerShell Core and will help you understand the very basics of PowerShell while the next chapters assume solid scripting knowledge.
In this chapter, we will cover the following recipes:
- Installing PowerShell Core on Windows
- Installing PowerShell Core on Linux
- Running PowerShell Core
- Getting help
- Getting around
- How do cmdlets work?
- Performing read-only operations
- Introducing change to systems
- Discovering the environment
In this recipe, you'll learn how to provision PowerShell Core on a Windows system starting with Windows 6.1 (Server 2008 R2/Windows 7).
To follow this recipe, you'll need a Windows machine with at least Windows Server 2008 R2 or Windows 7. If this machine isn't connected to the internet, you'll need a way of transferring the installer to the machine.
Please perform the following steps:
- In order to get the most recent release of PowerShell Core for Windows, browse to https://github.com/powershell/powershell:

- Download the release for your platform. I recommend using the stable 64 bit (x64) edition if possible.
Â

- Start PowerShell Core by typing
pwsh
into the search bar!
Using the standard MSI installer methods, PowerShell Core will be installed for your system in the 64 bit Program Files
directory by default. It won't replace Windows PowerShell but will simply coexist peacefully and include the most recent updates to PowerShell.
If left on the default settings, an event manifest will be registered on the system, enabling an event log for PowerShell Core. The log file will be placed at %SystemRoot%\System32\Winevt\Logs\PowerShellCore%4Operational.evtx
and can be found in the Applications and Services Logs
.
By default, no remoting configuration will be made. We'll talk about remoting in Chapter 8, Running Remote Commands and Understanding Just Enough Administration, where you'll enable PowerShell remoting for a system.
Â
Besides the installer that you can download and install manually or through any software deployment solution, you can also use Chocolatey. Chocolatey is a NuGet package source for binary packages and can be used to bootstrap software on a system.
The following steps will install Chocolatey and PowerShell Core on a Windows system. These steps require using Windows PowerShell for the initial process:
- Run the following command in Windows PowerShell (see https://chocolatey.org/docs/installationfor details):Â
Set-ExecutionPolicy Bypass -Scope Process -Force iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
- After the installation of Chocolatey, simply executeÂ
choco install powershell /y
. - Start PowerShell Core by searching for
pwsh
in the search bar!
If you're so inclined, compiling the code from scratch is of course also an option. Simply follow the guidelines laid out in the GitHub repository to do so:
# Clone the repository git clone https://github.com/powershell/powershell Set-Location -Path .\powershell Import-Module ./build.psm1 # Ensure you have the latest version of .NET Core and other necessary components Start-PSBootStrap # Start the build process Start-PSBuild # Either run PowerShell directly... & $(Get-PSOutput) # ...or copy it to your favorite location (here: Program Files on Windows, necessary access rights required) $source = Split-Path -Path $(Get-PSOutput) -Parent $target = "$env:ProgramFiles\PowerShell\$(Get-PSVersion)" Copy-Item -Path $source -Recurse -Destination $target
Â
Â
Â
Â
Â
Â
Â
Â
Â
Â
- PowerShell documentation:Â https://docs.microsoft.com/powershell
- Chocolatey documentation:Â https://chocolatey.org/docs/installation
Since the arrival of PowerShell Core, its key feature has been the ability to run cross-platform and provide the exact same experience on any operating system. Installing PowerShell on Linux is nearly as easy as it is in Windows. If your distribution is among the list of supported distributions such as CentOS, openSUSE, or Ubuntu, the process is pretty straightforward.
In order to follow the recipe, you'll need any Linux distribution (even the Windows Subsystem for Linux) that's preferably connected to the internet and can download packages. In the recipe, I'm using CentOS and Ubuntu to show some very different approaches.
At the time of writing, the recipe was correct. However, check whether it still applies on the official installation page for your operating system, for example, https://docs.microsoft.com/en-us/powershell/scripting/setup/installing-powershell-core-on-linux.
On CentOS 7.x, perform the following steps:
- Register the Microsoft RPM repository:Â
curl https://packages.microsoft.com/config/rhel/7/prod.repo | sudo tee /etc/yum.repos.d/microsoft.repo.
- Install the package:Â
sudo yum install -y powershell
. - Run PowerShell by executing
pwsh
, which is now installed.
On Ubuntu 18.10, perform the following steps:
- Open the Ubuntu Software store.
- Search for
powershell
:

- Install and run PowerShell Core:

The steps to install PowerShell on other distributions are fairly similar as long as you can use your distribution's package management system, such as rpm
or dpkg
.
Usually, the installation instructions for Linux require the registration of a package repository that's maintained by Microsoft and used to publish PowerShell Core. The repository settings include the URL, a reference to the GPG public key, and the status of the repository.
On Linux, binary packages are usually compiled using a makefile on the running OS and then, for example, linked or copied to one of the binary paths. The best example for this is probably Gentoo, where compiling your kernel and all components, libraries, and software is actually required. With RPM and DEB packages, developers can better resolve dependencies and include all necessary instructions to install a binary package or compile a source package.
With binary packages, the component is compiled for a specific architecture with general compilation flags set. While this won't allow the user to fine-tune every part of the installation, it'll provide the benefit of an easier deployment.
PowerShell comes pre-built in, for example, an RPM package for different OS architectures. By using the package management provider of the distribution, you ensure that all necessary dependencies are installed alongside the package itself.
With Ubuntu 18.10, PowerShell is available as a Snap package in the Ubuntu Software store. This allows a more user-friendly installation of PowerShell that doesn't require the command line at allâapart from using PowerShell, of course.
There're many different flavors of Linuxâthere is macOS, Windows, and probably other platforms to come. Stay up-to-date by having a look at the official installation instructions at https://github.com/powershell/powershell.
In addition to traditional installation methods, you can also build your entire PowerShell from scratch.
- Information on the Snap package format:Â https://snapcraft.io
- The official package source for PowerShell packages:Â https://github.com/powershell/powershell
Using PowerShell Core is very simple. This recipe will show you the very first steps and help you to run PowerShell Core after the installation.
In order to follow this recipe, you should have completed the installation of PowerShell Core for your operating system.
Let's perform the following steps:
- On Windows, run
pwsh.exe
. On Linux or macOS, runpwsh
. - Type your first cmdlet,
Get-Process
, to retrieve a list of running processes on the system and hit Enter to confirm. - Compare the output of the cmdlet with the output ofÂ
tasklist
(in Windows) andps
 (in Linux):

- Type
Get-Date
and hit Enter to confirm.

- Execute the line:Â
Get-Process | Where-Object -Property WorkingSet -gt 100MB
. - Compare the output again with the output ofÂ
tasklist /FI "MEMUSAGE gt 102400"
 (in Windows) andps -aux | awk -F" " '$5 > 102400'
 (in Linux):

- Lastly, execute this cmdlet:Â
Stop-Computer -WhatIf
. This time, there's no comparable command on either Windows or Linux.
PowerShell works with commands like any other shell environment. Native PowerShell commands are called cmdlets. Unlike commands from other shells, PowerShell cmdlets should only serve one purpose and fulfill this purpose only. As always, there're exceptions to the rule. In some cases, command-line switches, called switch parameters, can be used to toggle additional functionality.
The first example, Get-Process
, returns (Get
) a list of running processes (Process
). While the formatted output appears similar to that of the Windows command tasklist, PowerShell doesn't merely return text, but .NET objects.
Our second example, Get-Date
, returns the current date and time as a .NET object again. In .NET, time is calculated with ticks, which are 100 nanosecond-intervals starting at 0001-01-01 00:00:00. The output is formatted depending on your operating system's culture and can be changed on demand.
The third example has you filter the output with PowerShell, which is extremely easy compared to Windows and Linux alike. Especially the endless possibilities of working with text in Linux make this a striking example. The ps
command doesn't allow much filtering, so you need to rely on tools such as awk
to process the text that is returned. This simple task without PowerShell requires knowledge of text processing and filtering with different tools.
The last cmdlet, Stop-Computer
, demonstrates a very common parameter with many cmdlets called WhatIf
. This parameter allows you to simply try a cmdlet before actually doing anything. This is an excellent way to test changes for general correctness, for example, before modifying your 10.000 Active Directory user accounts:

There's plenty more to do and see in PowerShellâpart of which will be covered in this book. Try to follow the upcoming recipes as well to find out about cmdlet discovery, the flow between cmdlets in the pipeline, and much more.
- All official documentation regarding PowerShell:Â https://docs.microsoft.com/powershell
- Learn PowerShell Core 6.0 by David das Neves and Jan-Hendrik Peters, as a supplementary book to really learn the language:Â https://www.packtpub.com/networking-and-servers/learn-powershell-core-60
Help is never far away in PowerShell Core and, in this section, you'll learn how to utilize the help to your benefit.
In order to follow this recipe, you should have completed the installation of PowerShell Core for your operating system.
Please perform the following steps:
- Open PowerShell Core.
- Type the
Get-Help
cmdlet and hit Enter. The cmdlet displays help about the help system.
- Use the
-?
parameter with any cmdlet, for example,ÂStart-Process -?
. Notice the output after this cmdlet. You can see the syntax of the cmdlet, as well as some additional remarks:


- Type the
Update-Help -Scope CurrentUser
command to download all current help content. - Examine the folder contents ofÂ
$home\Documents\PowerShell\Help
 in Windows and Â~/.local/share/powershell
in Linux. - Type the
Update-Help -Module CimCmdlets -UICulture ja-jp,sv-se
 command. Notice that not all modules provide localized help contentâthe content inen-us
should be available for most modules, however. - Now that the help content has downloaded, try
Get-Help Start-Process -Full
again. - Notice that now the full content is available, allowing you to get additional information about a cmdlet.
The help system of PowerShell Core can be used to update help files from the internet or from a CIFS
share. Without updated help content, the help system always displays the name and syntax of a cmdlet as well as detailed parameter help for all parameters of a cmdlet.
In order to update help for modules on the local system, Update-Help
will examine all modules in the PSModulePath
environmental variable in order to find all modules that have the HelpInfoUri
property set. It'll try to resolve the URI, which should point to a browsable website where it will then look for an XML file called <ModuleName>_<ModuleGuid>_HelpInfo.xml
. Inside this XML file, the location of a cabinet file (*.cab
) is stored, which will then be used to download the actual content.
With the new Scope
parameter introduced in PowerShell Core, all help content will be placed in the personal user folder, for example, C:\Users\<UserName>\Documents\PowerShell
, instead of a system-wide folder that would require administrative privileges, for example, C:\Program Files\PowerShell
.
The Update-Help
cmdlet will only download new content once per day if the cmdlet is called. In order to download the content more frequently, you can use the Force
parameter.
Help content can also be hosted on-premises by using the Save-Help
cmdlet and distributing the content. On Windows systems, a group policy setting can be found that can control the default path for Update-Help
as well. This setting is in Administrative Templates
| Windows Components
| System
| Windows PowerShell
. This setting is only valid for Windows PowerShell. Regardless of the edition, the Update-Help
cmdlet supports the SourcePath
parameter to specify from where the help content will be downloaded.
In order to provide your own help content properly, have a look at the PowerShell module, PlatyPS
. This module makes it very easy to generate help content for your own modules, package it to the correct format, and much more.
PlatyPS
supports markdown help, enabling you to write help content in a very easy way that feels more natural than creating large and complex MAML
files.
- Help overview:Â https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_updatable_help?view=powershell-6
- PowerShell module to generate help content:Â https://github.com/powershell/platyps
In this section, you'll learn how to get around on any system running PowerShell Core through cmdlet discovery.
In order to follow this recipe, you should have completed the installation of PowerShell Core for your operating system.
Please perform the following steps:
- Open PowerShell Core.
- Type
Get-Command
. Notice that all available cmdlets on the system will be displayed. Depending on the modules installed on your system, this will be a lot. - Type
Get-Command New-Item -Syntax
. Notice that, this time, it's not the cmdlet that's returned, but the syntax that's displayed:

- Type
Get-Command -Verb Get -Module Microsoft.PowerShell.Utility
. Notice here that all read-only cmdlets of a specific module are returned, thereby greatly narrowing down the results:

- Type
Get-Command -CommandType Application
. This time, all external applications (in other words, binaries) are returned. Try to favor native PowerShell cmdlets over external applications where possible:

- Type
Get-Command -ParameterName ComputerName,CimSession,PSSession
. This is one of my favorites; with this parameter, only cmdlets that have certain parameters are returned. In this instance, all remote-capable cmdlets will be returned. This parameter, however, only searches through all cmdlets available in the current session:

- Type
Get-Command *Process,*Item
. Notice that, this time, a wildcard search is performed on all cmdlets that exist on the system. - Type
New-Alias -Name Start-Process -Value hostname
and then typeGet-Command Start-Process
. Only the alias will be returned now, effectively hiding the cmdlet,ÂStart-Process
. - Type
Get-Command Start-Process -All
. This time, the alias as well as the original cmdlet are returned.
PowerShell and its incredibly flexible system are easily discovered with the help of Get-Command
. Even for a seasoned PowerShell expert, Get-Command
is invaluable as it works on any system, doesn't need additional content, and will save you precious time. Additionally, nobody is able to just know all existing cmdletsâsometimes, you just need to have a short look at the syntax.
Aliases are a part of PowerShell as well as cmdlets. Sometimes, an alias is introduced when the name of a cmdlet changes in order maintain backward compatibility to some degree. Other aliases are simply created to make working interactively faster or to ease the migration from another scripting language such as the aliases, dir
and ls
.
By inspecting module manifests and module definitions, Get-Command
is able to discover the exported cmdlets of a module that make up the available cmdlets on a system. Additionally, the PATH
environmental variable is used to discover external applications such as executables, libraries, and text files.
The output of Get-Command
can simply be filtered with wildcards in order to discover cmdlets that have a certain purpose, for example, *Process
will list all cmdlets that have something to do with processes.
One parameter that you should always use is the Syntax
parameter. Reading the cmdlet syntax is one of the easiest ways to determine how the cmdlet can be used, what its mandatory parameters are, and what its parameter values should look like.
Even if you're an advanced PowerShell user, Get-Command
can help you. Just have a look at the amount of data you can access for each command by using Format-List
. We'll later learn about Get-Member
as well:
# Discover more about a cmdlet with Format-List Get-Command New-Item | Format-List -Property * # Examine additional properties that might be helpful $cmd = Get-Command New-Item # Where does the cmdlet's help content come from? $cmd.HelpUri # Quickly jump to the location of a cmdlet's module Set-Location -Path $cmd.Module.ModuleBase # How many parameters does a cmdlet have including the common parameters? $cmd.Parameters.Count # Discovering the data of a parameter, in this case realizing that # New-Item allows empty strings or $null to be passed to the Name parameter $cmd.Parameters.Name
Look at the following screenshot of how the output looks:

- Â Information about command precedence and the way
Get-Command
displays its results:Â https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_command_precedence?view=powershell-6
In contrast to native OS commands such as ps
on Linux or tasklist
on Windows, PowerShell uses cmdlets. These cmdlets always follow the same, simple syntax. Moreover, specifying parameters and their values always works the same with every cmdlet as well.
This section will help you to understand how cmdlets work before diving into them in later chapters.
In order to follow this recipe, you should have completed the installation of PowerShell Core for your operating system.
Please perform the following steps:
- Start PowerShell Core.
- Type
New-Item -Path variable: -Name myVariable -Value "Isn't it great?
. - Type
$myVariable
.$myVariable
is a variableâa temporary storage space for your data. Just executing the variable will place it on the output. - Type
Get-ChildItem $home *.*
. This cmdlet uses no parameter names, and only parameter values.$home
is a built-in variable and*.*
filters for all files with a dot in the name. - Type
Get-ChildItem *.txt $home
. Observe the error this time. You can't mix positional parameters. - Type
Get-ChildItem -Filter *.txt -Path $home
. By using the parameter names, the cmdlet works again. - Type
$processName = 'powershell'
. Assigning anything to a variable like this will store the result in the variable. - Type
Get-Process $processName
. - Type
Get-Process $pid
. As opposed to the process name, using an ID will fail the cmdlet. - Type
Get-Process -Id $pid
. By using the correct parameter, the cmdlet works again. - Type
Get-Command -Syntax -Name Get-Process
. Observe the syntax of the cmdlet; there's more than one way to execute a cmdlet. These are called parameter sets.
Native PowerShell cmdlets should all follow the exact same syntax: verb-noun. The verb indicates the action and the noun indicates the recipient of that action. Whether it is a cmdlet such as New-Item
or Get-Process
, the syntax always follows the same principle.
With all of its different parameters used, a full cmdlet call might look like the following example:
Get-ChildItem $home -Filter *.txt -File
Get-ChildItem
is the name of the cmdlet. $home
is the value of a so-called positional parameter, Path
. -Filter
uses the parameter name, and *.txt
is the value provided for that parameter. -File
is something called a switch parameter, which resembles a command-line switch.
Very often, PowerShell is used to gather data for reporting purposes, exporting and viewing configurations, and more. This is generally accomplished using the various Get
cmdlets. In Chapter 2, Reading and Writing Output, we'll then see how to further process the gathered data.
Since the Get
cmdlets won't change anything on your system, this is a great way to discover what PowerShell has to offer.
In order to follow this recipe, you should have completed the installation of PowerShell Core for your operating system. Some cmdlets are Windows-specific and require a Windows operating system.
Please perform the following steps:
- On a Windows system, you can use many built-in cmdlets with PowerShell Core. Try
Import-Module Storage -SkipEditionCheck
. - On a Windows system, type
Get-Disk
 to list all disks. The result should look similar to the following:


- On any system, try theÂ
Get-Uptime
 cmdlet to calculate the system uptime. - Use
Get-Culture
andGet-UICulture
to view the current language settings. - Review the result of
Get-PackageProvider
. On a Windows system, additional providers are visible.
PowerShell provides access to several different data sources by means of the Get
cmdlets. After retrieving the data, PowerShell wraps it into an object model to allow you to store, display, filter, and process the data.
The data sources can be anything from services, event logs, and files to functions and variables. Every item that's retrieved is called an object and will have different properties and methods, which we will see later on.
Just explore Get-Command -Verb Get
to find all read-only cmdlets and simply have a look at what the return values are. There's no harm in trying!
On your way to building a perpetuum mobile? Make PowerShell execute all Get
cmdlets for some fun:
$ErrorActionPreference = 'SilentlyContinue' Get-Command -Verb Get | ForEach-Object { & $_ }
The ampersand operator will invoke an expression. By iterating over each Get
cmdlet that is returned by Get-Command
, we try to read everything we can get our hands on. Please be aware that this will take some time.
While reading data is usually fine, PowerShell is also a great automation engine that's able to change a system configuration. We'll explore a couple of cmdlets that will, in some form, change your system configuration.
In order to follow this recipe, you should have completed the installation of PowerShell Core for your operating system. You should prepare a virtual machine for testing purposes since the cmdlets used in this recipe will inadvertently change your system configuration.Â
Please perform the following steps:
- Review the output of
Get-Command -Verb New,Set,Remove,Register,Unregister,Start,Stop
to review some of the more frequently used cmdlets. - Execute
$file = New-TemporaryFile
to create a temporary file. - Use
'SomeContent' | Set-Content -Path $file
 to change the file contents. - Use
'More content!' | Add-Content -Path $file
 to append data to the file. - Review the contents with
$file | Get-Item | Get-Content -Path
. - Lastly use
$file | Remove-Item -Verbose
to get rid of the file again. - Use
$ping = Start-Process -FilePath ping -ArgumentList 'packtpub.com' -PassThru
. - Use
$ping | Stop-Process -PassThru
to stop the background process.
- Use
Start-Job -Name Sleepy { Start-Sleep -Seconds 100; Get-Date}
. - Have a look at the job with
Get-Job -Name Sleepy
âis it ready to deliver the data? - Use
Get-Job -Name Sleepy | Wait-Job
to wait for the results. - Lastly, use
Get-Job -Name Sleepy | Receive-Job -Keep
to gather the results. - As an alternative, try
$job = Get-ChildItem -Recurse -Force -Path $home &
and$job | Wait-Job | Receive-Job
. - Clean up any remaining jobs by closing PowerShell or executing
$job | Remove-Job; Get-Job -Name Sleepy | Remove-Job
.
There're many verbs in PowerShell that indicate changes such as New
, Set
, and Remove
. Many of those cmdlets also return objects for the data that's altered or created. If one of those parameters doesn't provide output such as Stop-Process
, you can try using the PassThru
parameter if it is available. It usually means that objects will be returned.
In the recipe, you can see the usual flow between different cmdlets. The file can be created, modified, and removed using the pipeline and cmdlets related to each other. In Chapter 2, Reading and Writing Output, we'll see how pipeline input is usually processed.
With the new parameter, &
, you can start a background job, much like the forking parameter on Linux. The job results can be collected later as well.
There're countless cmdlets that change a running system, some of which we will see in this book. Be sure to have a look at the PowerShell repository on GitHub and the documentation on https://docs.microsoft.com/en-us/Â to get all available information before ruining your weekend or your colleague's on-call shift.
- The official documentation:Â https://docs.microsoft.com/powershell
- The official code:Â https://github.com/powershell/powershell
PowerShell Core has plenty of built-in variables that give you immediate information about the environment you are working with. The following recipe will show you the most important ones.
In order to follow this recipe, you should have completed the installation of PowerShell Core for your operating system.
Please perform the following steps:
- Review the output of
$PSVersionTable
. With this variable, you'll always know which version and edition you are running. - Try to execute
Set-Location $PSHome; Get-ChildItem
. This folder contains all PowerShell binaries necessary to run the shell. - Have a look at the value of
$pid
. This variable always points to your own PowerShell process. - Try running the
Get-Item DoesNotExist
cmdlet and afterward, view the contents of$Error
. This variable collects errors that happen in your session. Not all errors collected here have been visible on the CLI. - Try the following:
$true = $false
. You'll be pleasantly surprised that these variables are so-called constants and can't be changed. - Have a look at the output of
Get-Process | Format-Table Name,Threads
. You'll notice that the threads always seem to stop at four elements. - Display the contents of the variable,Â
$FormatEnumerationLimit
. The value of four isn't a coincidence. This variable governs how list output is formatted. - Have a look at
$PSScriptRoot
. For some reason, this variable is empty. The reason is that this variable is only set when a script is executed. It then will point to the directory containing the script.$PSCommandPath
will contain the entire script path.
- Run the following command:
Set-Content -Path ~/test.ps1 -Value '$PSScriptRoot;$PSCommandPath'; ~/test.ps1
. Examine the output; the first line contains the script directory, whereas the second line will show the full script path you executed. - Lastly, try
Get-Variable *Preference
. These variables control the behavior of PowerShell regarding errors, warnings and more. In Chapter 2, Reading and Writing Output, we'll have a close look at those.
Each time a new PowerShell session is started, a bunch of variables is registered and filled. You can always rely on those variables to exist and be present in your scripts. Many of those variables contain preferences for cmdlets, formatting, and output.