About PowerShell
Windows PowerShell is Microsoft's scripting language of choice. Windows PowerShell is a task-based command-line shell and scripting language designed especially for system administration. Built on the Microsoft .NET Framework, Windows PowerShell helps IT professionals and power users control and automate the administration of the Windows operating system and applications that run on Windows.
An advantage of PowerShell is the ability to execute ordinary Windows binaries. You can execute Windows commands from within PowerShell, no matter whether it is appcmd, dir
, forfiles, or advanced netsh
commands.
Because it's built on the .NET Framework, you can even use .NET functions and functionality. For example to make an SMTP connection:
(New-Object System.Net.Sockets.TcpClient).Connect( "smtp.example.com", "25" )
Built-in Windows PowerShell commands are called cmdlets. They let you manage the computers in your enterprise from the command line. Windows PowerShell providers let you access data stores, such as the registry and certificate store, as easily as you access the file system. In addition, Windows PowerShell has a rich expression parser and a fully-developed scripting language.
Cmdlets
A cmdlet is a lightweight command that is used in the Windows PowerShell environment. The Windows PowerShell runtime invokes these cmdlets within the context of automation scripts that are provided at the command-line. The Windows PowerShell runtime also invokes them programmatically through Windows PowerShell APIs.
The Get-WMiObject Cmdlet - Retrieving Data Using WMI
In scripts you often see the use of Get-WMiObject
cmdlet. WMI stands for Windows Management Instrumentation. You may wonder why there is so much WMI usage in PowerShell, Microsoft explains:
At this point in time there are only a few cmdlets (
Get-Process
,Get-Service
, andGet-EventLog
) designed for carrying out system administration tasks. Because of that, WMI remains the primary automation technology for system administration, so system administrators will likely rely heavily on Get-WmiObject to help them with their routine management tasks.And there’s an added bonus, too: unlike most cmdlets, Get-WmiObject can be run against remote computers. That means you really can use Windows PowerShell as a management tool.
By default, Get-WmiObject binds to the root\cimv2 namespace on the local computer, making it extremely easy to return property values for any class found in cimv2.
As you can see, for Windows administration with PowerShell, a basic understanding of WMI is required.
Scripting in Windows PowerShell
One task you can use Windows PowerShell for is scripting: automate your daily tasks using simple PowerShell scripts. One of such is, for example, a PS script to monitor Windows services like LanmanWorkstation. Another to find all files owned by a particular user. These two examples should provide some insights in the earlier mentioned cmdlet usage in PS. As said, you can also combine PowerShell with your normal Windows commands in one script.
Command-line arguments in PS
A PS script can accept command-line arguments. Imagine an unattended installation script to automate the installation of components of your web server. For example an script to install PHP. Now, if you want to add an option to either update or install PHP, as a command-line argument, add the following at the top of your PS script (different styles and options for adding command-line arguments support are possible):
# initialization
$mode = $args[0]
$arguments = @{
"-i" = "install PHP";
"-u" = "update PHP";
"-v" = "PHP version information";
}
# ...
# ...
$map = $args[1]
if ( $mode -and $arguments.ContainsKey( $mode ) ) {
# we've added a php-version.txt file containing
# PHP's version number, e.g 7.0.5
if ( ( test-path php-version.txt ) ) {
$version = get-content php-version.txt
}
set-variable -name versie -value $version -scope script
if ( ( $mode -eq "-i" ) ) {
install ( $version )
}
if ( ( $mode -eq "-u" ) ) {
update ( $version , 0 )
}
if ( ( $mode -eq "-v" ) ) {
$return = version ( 1 )
}
}
else {
write-host "[i] Usage:"
foreach ( $s in $arguments.Keys ) {
write-host "[i] $s " $arguments[$s]
}
}
Now when you call your script install-php.ps1
with a command-line argument -i
, the install
function is executed, installing PHP. Want to know the current installed PHP version? Let's have a look at the version
function:
function version ( $returnerror ) {
if ( test-path $target\php-version.txt ) {
# open $target\php-version.txt containing
# the version number
$found = get-content $target\php-version.txt
write-host "[*] Version information for $target"
write-host "[i] $found"
return $found
}
else {
if ( $returnerror ) {
write-host "[E] Error: PHP is not installed!"
}
return 0
}
}
As you can see, adding command-line arguments support into your PowerShell scripts may make your script library easier to understand. Now we have one PowerShell script for the installation of PHP, to update PHP and to look up its version. Otherwise that would have been three separate scripts.
You can easily do the same when installing Windows Server features:
# Accept a $feature argument to install an specific
# Windows Server feature
param (
[string]$feature = $(throw "-feature is required.")
)
Add-WindowsFeature `
-Name $feature `
-Source \path\to\windows\2012r2\sources\sxs
If ... Else, If ... ElseIf ... Else logic
Flow control with If, ElseIf and Else in Windows PowerShell. Using If, ElseIf, Else you decide what a script has to do at a certain point, when a condition is met. An If is often used in an evaluation or comparison, e.g If $foo = bar
, do this, Else
do something else... He, that's logic! :-)
Let's have a look at the following example:
# If .. ElseIf .. ElseIf .. Else .. in PowerShell
param (
[string]$a = $( throw "please provide a number 1 .. 6" )
)
If ( $a -eq "1" ) {
write-host "Your number of choice: 1"
}
ElseIf ( $a -eq "2" ) {
write-host "Your number of choice: 2"
}
ElseIf ( $a -eq "3" ) {
write-host "Your number of choice: 3"
}
ElseIf ( $a -eq "4" ) {
write-host "Your number of choice: 4"
}
ElseIf ( $a -eq "5" ) {
write-host "Your number of choice: 5"
}
ElseIf ( $a -eq "6" ) {
write-host "Your number of choice: 6"
}
Else {
write-host "Can't read?! I asked for a number between 1 and 6!"
}
Playing with this, the output is:
PS C:\Users\jan> .\Desktop\if-else.ps1
please provide a number 1 .. 6
At C:\Users\jan\scripts\if-else.ps1:3 char:18
+ [string]$a = $( throw "please provide a number 1 .. 6" )
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (please provide a number 1 .. 6:String) [], RuntimeException
+ FullyQualifiedErrorId : please provide a number 1 .. 6
PS C:\Users\jan> .\scripts\if-else.ps1 1
Your number of choice: 1
PS C:\Users\jan> .\scripts\if-else.ps1 3
Your number of choice: 3
PS C:\Users\jan> .\scripts\if-else.ps1 5
Your number of choice: 5
PS C:\Users\jan> .\scripts\if-else.ps1 6
Your number of choice: 6
PS C:\Users\jan> .\scripts\if-else.ps1 9
Can't read?! I asked for a number between 1 and 6!
PS C:\Users\jan>
Not very hard, but you can imagine a script like this can get very long and difficult to read.
PowerShell Switch statement
The switch statement in PowerShell enables you to write a script that can choose from a series of options, but without requiring you to write a long series of if statements. When having a large If ( ) {.. } ElseIf ( ) {.. } ElseIf ( ) {.. } Else {.. }
block in your scripts, it's often better to rewrite those to a Switch statement. Not only makes this your PowerShell script execute faster (switch often is), it makes your script easier to read as well. Let's rewrite the above If .. ElseIf .. ElseIf .. Else ..
logic.
# Switch statement in PowerShell
param (
[string]$a = $( throw "please provide a number 1 .. 6" )
)
switch ( $a ) {
1 { "Your number of choice: 1" }
2 { "Your number of choice: 2" }
3 { "Your number of choice: 3" }
4 { "Your number of choice: 4" }
5 { "Your number of choice: 5" }
6 { "Your number of choice: 6" }
default { "Can't read?! I asked for a number between 1 and 8!" }
}
As you can see, the same script rewritten with a Switch instead of If statement is much shorter and easier to read/understand. It outputs:
PS C:\Users\jan> .\scripts\switch.ps1
please provide a number 1 .. 6
At C:\Users\jan\scripts\switch.ps1:3 char:18
+ [string]$a = $( throw "please provide a number 1 .. 6" )
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (please provide a number 1 .. 6:String) [], RuntimeException
+ FullyQualifiedErrorId : please provide a number 1 .. 6
PS C:\Users\jan> .\scripts\switch.ps1 1
Your number of choice: 1
PS C:\Users\jan> .\scripts\switch.ps1 5
Your number of choice: 5
PS C:\Users\jan> .\scripts\switch.ps1 3
Your number of choice: 3
PS C:\Users\jan> .\scripts\switch.ps1 6
Your number of choice: 6
PS C:\Users\jan> .\scripts\switch.ps1 66
Can't read?! I asked for a number between 1 and 8!
PS C:\Users\jan>
PowerShell functions
A function in PowerShell is like a function in any other language, whether it's C#, PHP, Bash. Within Powershell you can reference functions within statements. A function is basically a named block of code. When you call the function name, the script block within that function runs.
You can include any PowerShell statements within the script block, and you can add input parameters so you can use the same function in different situations. Let's look at how to create functions, define input parameters, and work with functions in PowerShell scripts.
Create a PowerShell function
A PowerShell function must have a function name, to call it by, and a script block between braces { }
. For example:
function HelloWorld {
Write-Host "Hello World in PowerShell!"
}
As you can see, this function definition begins with the function
keyword, followed by the function's name: HelloWorld. The script block includes just one command, it echos the string "Hello World in PowerShell!". When you create a function, PowerShell stores it in memory for the duration of your session. During that session, you can call the function at any time by simply entering the function's name, as in HelloWorld
. After pressing ENTER in the PowerShell shell, or from the point in your script, the function block is run and the output presented. In this case the above mentioned string, but it can be anything.
Let's add the function "HelloWorld" beneath the function, and execute it as a PS script. The script becomes:
function HelloWorld {
Write-Host "Hello World in PowerShell!"
}
HelloWorld
And it'll output:
PS C:\Users\jan> .\scripts\helloworld.ps1
Hello World in PowerShell!
Powershell functions also accept parameters:
function testFunction ($arg1, $arg2) {
Write-Host $arg1
Write-Host $arg2
}
There are pitfalls when calling function parameters though! In Calling Functions in PowerShell, Thomas Lee provides a succinct example of one pitfall that even experienced PowerShell scripters will occasionally tumble into-writing a basic function call that produces unexpected results. Read Michael Sorens' simple-talk.com article Down the Rabbit Hole- A Study in PowerShell Pipelines, Functions, and Parameters
PowerShell Comparison operators - compare values in PowerShell
PowerShell supports a number of comparison operators. The following operators are all case-insensitive by default:
operator | explanation |
---|---|
-eq |
Equal |
-ne |
Not equal |
-gt |
Greater than |
-ge |
Greater than or equal |
-lt |
Less than |
-le |
Less than or equal |
-Like |
Wildcard comparison |
-NotLike |
Wildcard comparison |
-Match |
Regular expression comparison |
-NotMatch |
Regular expression comparison |
-Contains |
Containment operator |
-NotContains |
Containment operator |
-In |
Like -contains, but with the operands reversed (PowerShell 3.0) |
-NotIn |
Like -notcontains, but with the operands reversed (PowerShell 3.0) |
-Replace |
Replace operator |
By default, all comparison operators are case-insensitive. To make a comparison operator case-sensitive, precede the operator name with a "c
". For example, the case-sensitive version of "-eq
" is "-ceq
". To make the case-insensitivity explicit, precede the operator with an "i
". For example, the explicitly case-insensitive version of "-eq
" is "ieq
".
Run Update-Help
and about_Comparison_Operators
to view its help information.
Windows Server administration, installation and automation with PowerShell
Windows PowerShell provides a lot of Windows Server administration cmdlets. Cmdlets to use when installing Windows Server, automate installations, DevOps, and so on. There are just too many to mention them all. Some important ones - to me, in my daily routine - are:
- Get-WmiObject
- Get-Service, Stop-Service, Start-Service
- Test-NetConnection (and because Windows PowerShell is built on .NET, you can also use
(New-Object System.Net.Sockets.TcpClient).Connect("localhost", "25")
instead ofTest-NetConnection -Port 25 -ComputerName localhost
. See monitor Windows services with PowerShell for an example) - Get-FsrmQuota, Set-FsrmQuota
- Get-ChildItem
- Get-Acl
- Install-WindowsFeature, Uninstall-WindowsFeature, Get-WindowsFeature (see Install IIS in Windows 11 using PowerShell for examples)
- AppCmd (not a Cmdlet, but very handy to use within Windows PowerShell to create websites and application pools)
- Get-website, Get-WebConfiguration, Get-WebConfigurationProperty (find more Web Server (IIS) Administration Cmdlets in Windows PowerShell on Technet)
Often, when there is a cmdlet starting with Get-, there is also one with Set-. In my daily routine, Set-FsrmQuota is as important to me as Get-FsrmQuota. The same goes for Get-Acl and Set-Acl, and so on.
Two example PS administration scripts:
find-all-PHP-handlers-matching-an-scriptProcessor-condition.ps1
Get-Website | % {
$sitename = $_.name; Write-Host "[X] Processing $sitename ... ";
Try {
Get-WebConfiguration /system.webServer/handlers/* -Location $_.name | Where {
$_.scriptProcessor -eq "d:\PHP\php56\php-cgi.exe"
}
}
Catch { }
}
Get-Website | % {
$sitename = $_.name;
$handlers = Get-WebConfiguration /system.webServer/handlers/* -Location $sitename
If ( $handlers.scriptProcessor -eq "d:\PHP\php70\php-cgi.exe" ) {
Write-Host $sitename
}
}
w3wp-uptime.ps1
Get-Process w3wp | % {
$pid = $_.Id
$wmiProc = Get-WmiObject Win32_Process -Filter "ProcessID = '$pid'"
New-Object PSObject -Property @{
CommandLine = $wmiProc.ComandLine
StartTime = $_.StartTime
}
}
(Code credits: Chris N on StackOverflow)
Conclusion
Windows PowerShell is powerfull, but unfortunately, PowerShell isn't as powerful and interactive as a shell as GNU/Linux's Bash is (in my opinion). However, you can still create very powerful PowerShell scripts to use in your daily Windows Server administration tasks. The number of available cmdlets for PowerShell is too large to mention them all here.
In this post I showed you a small fraction of those cmdlets, and gave you some beginner insights in PowerShell usage for Windows Server administration, automation and and shell/command-prompt scripting.