Does PowerShell Support Object-oriented Programming (OOP)?

Yes, PowerShell is built on the top of the .NET framework, so it is safe to say that it supports Object-oriented Programming. A good definition of PowerShell is:

“Windows PowerShell is the new standard Windows command-line shell. Windows PowerShell includes a runtime engine, data providers, core commands (known as cmdlets), a new scripting language, and an interactive prompt. Windows PowerShell is completely object-oriented and processes cmdlets, script files (.ps1), and executable files.” Developing with Windows PowerShell

Before PowerShell 5.0, this was not entirely true. It would be more accurate to use a phrase that my fellow MVP Keith Hill used: “PowerShell is more of an OOP consumer language.”

One of the most exciting and powerful features that were introduced into PowerShell 5.0 was the support for building .NET classes. In the previous versions, the simplest way to create custom objects in PowerShell was by using the [PSCUSTOMOBJECT] or new-object and build a .NET class in C# and, with the add-type cmdlet, import it to PowerShell.

For instance:

Before PowerShell 5.0:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 Function New-Sith { param ( [ Parameter ( Mandatory = $true ) ] [ string ] $Name , [ Parameter ( Mandatory = $true ) ] [ ValidateSet ( 'beginner' , 'Good' , 'VeryGood' , 'Master' ) ] [ string ] $KnowldgeOfTheForce ) New-Object psobject -property @ { Name = $Name KnowldgeOfTheForce = $KnowldgeOfTheForce Color = $color HeSheIsBad = $HeSheIsBad } } Function Invoke-ImperialMarch { param ( [ Parameter ( Mandatory = $true , Position = 0 , ValueFromPipelineByPropertyName = $true ) ] [ String ] $Name ) process { "{0} says : I find your lack of PowerShell disturbing..." -f $Name #original imperial march code from Jeff Wouters #http://jeffwouters.nl/index.php/2012/03/get-your-geek-on-with-powershell-and-some-music/ [ console ] :: beep ( 440 , 500 ) [ console ] :: beep ( 440 , 500 ) [ console ] :: beep ( 440 , 500 ) [ console ] :: beep ( 349 , 350 ) [ console ] :: beep ( 523 , 150 ) [ console ] :: beep ( 440 , 500 ) [ console ] :: beep ( 349 , 350 ) [ console ] :: beep ( 523 , 150 ) [ console ] :: beep ( 440 , 1000 ) [ console ] :: beep ( 659 , 500 ) [ console ] :: beep ( 659 , 500 ) [ console ] :: beep ( 659 , 500 ) [ console ] :: beep ( 698 , 350 ) [ console ] :: beep ( 523 , 150 ) [ console ] :: beep ( 415 , 500 ) [ console ] :: beep ( 349 , 350 ) [ console ] :: beep ( 523 , 150 ) [ console ] :: beep ( 440 , 1000 ) } } New-Sith -Name 'Darth Laertus' -KnowldgeOfTheForce Master | Invoke-ImperialMarch PowerShell 5 . 0 Class Sith { [ String ] $Name ; [ String ] $KnowldgeOfTheForce ; Sith ( [ String ] $NewName , [ String ] $NewKnowldgeOfTheForce ) { $this . Name = $NewName ; $this . KnowldgeOfTheForce = $NewKnowldgeOfTheForce ; } } $NewSith = ( [ Sith ] :: new ( 'Darth Laertus' , 'Master' ) ) $NewSith | Invoke-ImperialMarch

You can implement a constructor, methods, whatever else you want; the entire class definition. Indeed, it was good news for PowerShell developers, but the real unspoken question when developers ask if a language is OOP supported is “Can I use PowerShell to write a full ERP system?

Actually, you can, but you would not want to. PowerShell is intended to simplify and automate administrative tasks on Windows-based systems. Of course, it supports, by legacy, programming languages concepts, but it is not its basic purpose. You can write, for instance, a full CRM system in C++ or even in PowerShell, but in my humble opinion, it is equivalent to using a Ferrari in a farm to carry the milk churns.

You can read more about the implementation of classes in PowerShell 5.0 here:

Is PowerShell’s Execution Policy a security layer?

No. The execution policy in PowerShell is part of the security strategy but merely prevents potentially malicious scripts being executed by accident and, with the ‘ allsigned ‘ policy ensures that scripts cannot be altered without your knowledge. By default, this setting is set to ‘ Restricted ‘, meaning that no PowerShell script file can be run but it will execute PowerShell code within the PowerShell console. If you try to run a .ps1 script from file with execution policy set to restricted, you will face the message:

1 2 3 4 File C : \ Laerte \ Test . ps1 cannot be loaded because running scripts is disabled on this system . For more information , see about_Execution_Policies at http : / / go . microsoft . com / fwlink / ? LinkID = 135170 . + CategoryInfo : SecurityError : ( : ) [ ] , ParentContainsErrorRecordException + FullyQualifiedErrorId : UnauthorizedAccess

… but if you copy code from the .ps1 file and paste it into the PowerShell console it will be executed.

Can I bypass the Execution Policy Setting?

Yes. You can execute a PowerShell script file by calling it from a .bat shell-script file: This will bypass the execution policy in the system. Actually there are several ways to bypass the execution policy, but in this example, it is achieved merely by calling PowerShell.exe using the ‘ bypass‘ option in the -ExecutionPolicy parameter.

1 PowerShell . exe -ExecutionPolicy Bypass -File c : \ laerte \ test . ps1

For more information about execution policies:

Can I use cmd (dos batch file) commands in PowerShell?

Yes, you can. PowerShell can launch external programs in almost the same way as cmd and it accepts all the cmd commands as legacy.

I saw, in a script, a variable defined as $script:myvariable. What does $script mean?

Variables, aliases, functions and drives in PowerShell all work within ‘scopes’. This simply means that they are visible – or available – and changeable (mutable) only in the part of a script that you intend. This prevents values being changed unintentionally. In PowerShell, we can define four types of scope: global , local, private and script

When you include an item (or items) within a scope, by running a script or function, creating a new session or starting a new instance of PowerShell, it will be visible only in the scope within which it was created and in any child scope. This rule does not apply to private scope. In the same way, it can only be accessed or changed in its original scope. If you use the same name for another item in a different scope, the original variable will be hidden under the new item, but will not be overridden or changed.

For instance, if you create a variable inside a function, you cannot access (or change) its value in the script level where the function was called.

Global Scope

The global scope is the scope created when PowerShell starts. Any Item that is created when PowerShell starts or created with $global will be visible in the entire session and in all other scopes, as long it is alive. It is only when your code enters a nested prompt, script, block or function that a child scope is created. You can force a variable to be in global scope by using the $global: prefix to the variable.

1 2 3 4 5 6 7 8 9 10 11 12 $Global : IamGlobalBaby = "Now you see me" function InsideMeHaveOtherScope { write-host $Global : IamGlobalBaby $Global : IamGlobalBaby = "I was changed in another scope because I am Global" } InsideMeHaveOtherScope $Global : IamGlobalBaby

Local Scope

The ‘local scope’ is the default scope when you create a variable with no scope defined. Any item that is created within a function is available only within the script of function.

Script Scope

It is available from all scopes within the script, or in any script called within the parent script. While the script is running, you can think as it is the same a Global scope

For instance:

1 2 3 4 5 6 7 8 9 10 11 12 13 $IamLocal = "Now you See me as local" Function InsideMeHaveOtherScope { $IamLocal = "Now you See me as script scope" write-host $local : IamLocal write-host $Script : IamLocal } InsideMeHaveOtherScope Write-Host '--------------------------' write-host $local : IamLocal write-host $Script : IamLocal

In the next example below, I show how the script and local prefixes work to change values. In this script, I first created a variable called IamScript in the Script scope. Inside the function, I could access the value but I then tried to change the value not specifying its scope, so it created a new variable, locally only to the function with the same name.

1 2 3 4 5 6 7 8 9 10 11 12 $Script : IamScript = "I am script scope" Function InsideMeHaveOtherScope { Write-host $Script : IamScript $IamScript = "Changed the value, but in another scope. It created a new variable" Write-host $Script : IamScript Write-host $IamScript } InsideMeHaveOtherScope

If I want to change the value of the variable, I need to specify the scope:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 $Script : IamScript = "I am script scope" Function InsideMeHaveOtherScope { Write-host $Script : IamScript $Script : IamScript = "Changed the value in the script scope, inside the function" Write-host $Script : IamScript } InsideMeHaveOtherScope Write-host $Script : IamScript

What are the symbols $_ and $_. ?

The $_ variable-name is placeholder. Actually every script language uses placeholders and variables to store data. In this case, we could say that it where the data that comes out from a pipeline is held. In PowerShell everything is an object, and when you refer to a $ _ , you are using the entire object that is coming from the pipeline: Of course, you can also also choose a property, method or any item from this object using $_.Property syntax .

1 2 3 4 Get-service | Where-Object { $_ . Name -like '*SQL*' }

In PowerShell 3.0, an alias to $_ was introduced, called $ PSItem

1 2 3 4 Get-service | Where-Object { $PSItem . Name -like '*SQL*' }

$_ actually works like $this except that it means ‘this object’. Just as you can use it in a pipeline, you can use it in a Switch statement or any other processing structure.

1 2 3 4 5 6 7 8 $MyInput = 'LaerteJunior' , 'TonyJunior' , 'SimpleTalkEditor' switch -wildcard ( $MyInput ) { < code data -mce -bogus = "1" > < / code > "L*" { "$_ begins with the letter 'L'" } "*Junior*" { "$_'s Dad was called $($_ -ireplace 'Junior','')" } }

Which would give…

1 2 3 LaerteJunior begins with 'L' LaerteJunior 's Dad was called Laerte TonyJunior' s Dad was called Tony

If Everything in PowerShell is an object, how can I know all the properties, methods, whatever… all these items from an object?

Use the cmdlet Get-Member

1 2 Get-Process | Get-Member

What is Variable Interpolation?

When you add a variable to a double-quoted string, PowerShell replaces the variable name by its value. This feature is called variable interpolation.

1 2 $MasterSith = "Darth Vader" "The most powerfull Sith ever is $MasterSith"

You can hit a problem with variable interpolation if you include an object property (or even a result of a method) in the string. The standard notation of (dot property ) or ( . myproperty ) does not work as expected . For this problem, PowerShell has a subexpression operator $(). This operator first evaluates and run what is inside the brackets to resolve it into a simple value before passing it to the string for interpolation.

So here is a PowerShell script that will hit problems

1 2 $SQLBrowser = Get-Service -Name "SQL Server Browser" "SQL Server Browser status is $SQLBrowser.status"

So you need to use the $() syntax. In this case, PowerShell will evaluate the $ SQLBrowser.status first, and then will interpolate the rest of the string.

1 2 $SQLBrowser = Get-Service -Name "SQL Server Browser" "SQL Server Browser status is $($SQLBrowser.status)"

1 2 3 4 5 PS C : \ Users \ Administrator > $SQLBrowser = Get-Service -Name "SQL Server Browser" "SQL Server Browser status is $($SQLBrowser.status)" SQL Server Browser status is Stopped PS C : \ Users \ Administrator >

Another very important point is that the variable interpolation only happens in double-quoted strings so if you will don’t need interpolation, then use a single-quoted string instead.

What is the {0} -f that I saw in some string codes?

This is string formatting using the PowerShell format operator. In PowerShell, you do not need to use “+” operator to concatenate strings. Actually is more in-tune with PowerShell syntax if you use interpolation or the -f (from format)

It is simple. You add the placeholder {N} and then type a command-separated list of values in order to be replaced. {0} represents the first value in the command-separated list, {1} the second and so on.

1 "Hello I am {0}, I am {1} and so on" -f "the first value" , "the second one"

1 2 PS C : \ Users \ Administrator > "Hello I am {0}, I am {1} and so on" -f "the first value" , "the second one" Hello I am the first value , I am the second one and so on

You can, of course, do this more simply with variable interpolation, but the Format operator is the one to use if you need to format numbers, currencies, scientific notations, and dates. You can specify precision, separators and any or all fields within a date. You can also use it to align text.

What are Scriptblocks?

Scriptblocks are one of most Powerful concept of PowerShell. By definition, is a collection of statements or expressions between “{” and “}” that can be used as a single unit. It can be stored in a variable, can be passed to function and can be executed remotely by the operator “&”

Syntax :

{<statement list>}

Like functions, a scriptblock can include parameters :

1 2 3 4 { param ( [ type ] $parameter1 [ , [ type ] $parameter2 ] ) }

And script blocks can include the DynamicParam, Begin, Process, and End keywords.

The Basics

1 2 $MyScriptBlock = { "Hey, I am alive" } & $MyScriptBlock

1 2 3 4 5 6 PS C : \ Users \ Administrator > $MyScriptBlock = { "Hey, I am alive" } & $MyScriptBlock Hey , I am alive PS C : \ Users \ Administrator >

Adding Parameters

1 2 $MyScriptBlock = { param ( $param1 ) "Hey, I am alive and I love $($param1)" } & $MyScriptBlock -param1 "Star Wars"

1 2 3 4 PS C : \ Users \ Administrator > $MyScriptBlock = { param ( $param1 ) "Hey, I am alive and I love $($param1)" } & $MyScriptBlock -param1 "Star Wars" Hey , I am alive and I love Star Wars

Running Remotely

1 2 3 $MyScriptBlock = { "Hey, I am alive and I love" } Invoke-Command -ComputerName MyComputer -ScriptBlock $MyScriptBlock

How can I change a name of a property or add a new one in a output of an cmdlet ?

You may use custom tables.

Custom tables are a especial type of formatting output that uses the constructor @{} with the elements Expression and Name inside it , separated by semi-colon, to customize the output :

1 @ { Expression = { } ; Name = < Name > }

Expression is the data you want to display. Note that it is a scriptblock, so you can only display a property or write an entire script inside it.

Name is the header to appear in the top of the column. But how it can be used? Let’s check some examples:

If you run a Get-Process you will have the output :

1 Get-Process

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 PS C : \ Users \ Administrator > Get-Process Handles NPM ( K ) PM ( K ) WS ( K ) VM ( M ) CPU ( s ) Id SI ProcessName -- -- -- - -- -- -- -- -- - -- -- - -- -- - -- -- -- -- -- -- -- -- -- -- - 282 16 8336 19360 . . . 99 0 . 70 7404 2 ApplicationFrameHost 181 11 7300 10852 . . . 97 89 . 06 9532 0 audiodg 676 820 30096 47972 282 . . . 63 . 73 2644 2 BitTorrent 158 16 5600 14428 111 827 . 42 10804 2 calendar 214 17 2924 9492 80 3 . 03 11160 0 CalendarServ 381 35 114600 115188 463 2 , 712 . 31 2500 2 chrome 240 22 38712 23528 230 12 . 69 4344 2 chrome 430 44 185204 185288 591 3 , 265 . 36 4564 2 chrome 276 31 91840 44128 358 32 . 25 6344 2 chrome 378 53 233440 218840 652 5 , 607 . 91 7840 2 chrome 348 43 206756 184964 581 3 , 644 . 78 8068 2 chrome

Well, the header names are a bit hard to understand. I am a Brazilian and want to just show the Handles and ProcessName Columns as they are, but I would like the ‘ ProcessName ‘ title translated to Portuguese :

1 2 3 Get-Process | Select handles , @ { Expression = { $_ . ProcessName } ; Name = "Nome Do Processo" }

1 2 3 4 5 6 7 8 9 10 11 12 PS C : \ Users \ Administrator > Get-Process | Select handles , @ { Expression = { $_ . ProcessName } ; Name = "Nome Do Processo" } Handles Nome Do Processo -- -- -- - -- -- -- -- -- -- -- -- 282 ApplicationFrameHost 181 audiodg 686 BitTorrent 158 calendar 214 CalendarServ 381 chrome

Now I want to add the property ComputerName :

1 2 3 Select handles , @ { Expression = { $_ . ProcessName } ; Name = "Nome Do Processo" } , @ { Expression = { "MyComputerName" } ; Name = "Computer Name" }

1 2 3 4 5 6 7 8 9 10 11 12 13 PS C : \ Users \ Administrator > Get-Process | Select handles , @ { Expression = { $_ . ProcessName } ; Name = "Nome Do Processo" } , @ { Expression = { "MyComputerName" } ; Name = "Computer Name" } Handles Nome Do Processo Computer Name -- -- -- - -- -- -- -- -- -- -- -- -- -- -- -- -- -- - 282 ApplicationFrameHost MyComputerName 181 audiodg MyComputerName 671 BitTorrent MyComputerName 158 calendar MyComputerName

As I said before, the Expression is a scriptblock so I can call a function inside it:

1 2 3 4 5 6 7 8 9 10 function Show-Something { param ( $param ) Write-Output "I am outputing something" } Get-Process | Select handles , @ { Expression = { $_ . ProcessName } ; Name = "Nome Do Processo" } , @ { Expression = { "MyComputerName" } ; Name = "Computer Name" } , @ { Expression = { Show-Something -param 'Foo' } ; Name = "Called a Function" }

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Get-Process | Select handles , @ { Expression = { $_ . ProcessName } ; Name = "Nome Do Processo" } , @ { Expression = { "MyComputerName" } ; Name = "Computer Name" } , @ { Expression = { Show-Something -param 'Foo' } ; Name = "Called a Function" } Handles Nome Do Processo Computer Name Called a Function -- -- -- - -- -- -- -- -- -- -- -- -- -- -- -- -- -- - -- -- -- -- -- -- -- -- - 282 ApplicationFrameHost MyComputerName I am outputing something 181 audiodg MyComputerName I am outputing something 666 BitTorrent MyComputerName I am outputing something 158 calendar MyComputerName I am outputing something 216 CalendarServ MyComputerName I am outputing something 380 chrome MyComputerName I am outputing something 240 chrome MyComputerName I am outputing something 430 chrome MyComputerName I am outputing something

Note : the Expression and Name elements can be shorted to E and N

1 2 3 4 5 Get-Process | Select handles , @ { E = { $_ . ProcessName } ; N = "Nome Do Processo" } , @ { E = { "MyComputerName" } ; N = "Computer Name" } , @ { E = { Show-Something -param 'Foo' } ; N = "Called a Function" }

What is dot-sourcing?

In simple words, it is the way that you can make available in your current scope such items as variables or functions from another script.

As we saw before, scripts or functions have their own scope. So if I start a PowerShell session and want to load items in the current scope and keep them visible in the session, the way you can do it is by dot-sourcing this script.

Let’s create a function called foo that just displays a message in the screen:

1 2 3 4 Function Get-Foo { Write-Host "Hey I am alive" }

Then in the PowerShell host call this function in a standard PowerShell notation (.\NameofTheScript):

If I try to load the function again as a native cmdlet (just the name of the function) I will get an error because it was visible only when was called and destroyed after that.

But if I dot source this function and then try to call as a native cmdlet, it will work. Everything inside that .ps1 file now is available in the current scope.

In PowerShell, I’m told we can navigate within the registry as if it were a file system. How can this be possible?

Amazing, huh?

Yes, in PowerShell we have providers. In simple words, they are little .NET programs built in in PowerShell that allow you work with data as a file system.

We can check all the PowerShell providers by using the Get- PSProvider cmdlet :

1 Get - PSProvider

In the first column we have the name of the provider, followed by Capabilities – what the provider supports – and then the driver to access this provider.

As I said, you will work with these as file system; so to access the registry provider, just use the CD Drive: notation

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 PS C : \ Users \ Administrator > cd HKLM : PS HKLM : \ > dir Hive : HKEY_LOCAL_MACHINE Name Property -- -- -- -- -- -- BCD00000000 HARDWARE SAM SECURITY SOFTWARE SYSTEM PS HKLM : \ >

Then it is just a matter of changing the “subfolders “to keep navigating within it. ( CD \ Software ..etc)

It is not only the registry, as you can see. You can do the same for variables, certificates, functions, Environment variables, aliases and so on ..

I was writing a function and, when checking other codes, I saw a CMDLETBINDING word. What is this good for?

It’s simple. You are saying to your function “Hey beautiful, please from now on work as a compiled cmdlet”.

When you define the cmdletbinding attribute in a function, PowerShell binds its parameters the same way as it does the C# compiled cmdlets. This means this function is now an advanced function and it has access to all the features of the compiled cmdlets, as common parameters.

Functions, Advanced Functions are a huge topic. Please refer to my previous article: The PoSh DBA: Grown-Up PowerShell Functions.

That’s it guys. I´ve tried to address some of the questions that I had when I first started out with PowerShell, but if your questions are not in this list, please don’t be shy, email me laertesqldba@outlook.com and I will do my best to help.