10/02/2013

Getting started with PowerShell

PowerShell was released by Microsoft long ago – on January 30, 2007. Despite this many developers, build engineers and system administrators continue to use more familiar batch scripts (aka bat or cmd scripts). If you are one of such persons this post may help change your attitude towards PowerShell.

Running your first PowerShell script

Most probably you get used to run cmd.exe. Try powershell this time:


If you saw PowerShell console previously you’ll immediately argue: why console background is black but now awesome blue? Well, this is because blue color is set in Windows shortcut properties, so you need run PowerShell from shortcut to get it:



Awesome!
Now you can use PowerShell console in a way as if it is cmd console. Type cd, dir, mkdir, echo, rm, etc. You may even start believing you are working in cmd console.


PowerShell team did their best to make transition from batch scripts to PowerShell scripts as easy as possible. If you are familiar with Linux bash scripts you’ll also be happy with PowerShell:


All this is possible because of PowerShell aliasing feature. Commands (in PowerShell they are called cmdlets, pronounced command-lets) have aliases:

Here we see that dir is simply an alias for Get-ChildItem cmdlet. And by default Get-ChildItem cmdlet has three aliases: dir, gci and ls. You can create new aliases if you wish. Let’s discover real names for all aliases we used in our first script:

PS C:\tmp> get-alias cd, echo, type, rm
CommandType     Name
-----------     ----
Alias           cd -> Set-Location
Alias           echo -> Write-Output
Alias           type -> Get-Content
Alias           rm -> Remove-Item

As you can see PowerShell cmdlet for navigating to new location is Set-Location, but it is so much simpler to just type cd.

Is PowerShell better than cmd?

Have you ever tried to write simple if statements in cmd script, or (tears on my eyes...) for loop?

REM print numbers from 1 to 10
for /l %x in (1, 1, 10) do echo %x

And that code works only if you write it in cmd console. If you try save it to a file and run the file (try) you'll be surprised. Here is PowerShell code analogue:

# print numbers from 1 to 10
for ($i=1; $i -le 10; ++$i) { echo $i }

The only strange part of that code is -le (you cannot use < and > in shell scripts), everything else is so simple and familiar.
With PowerShell you have full power of .NET in your hands. Cmd reminds about old DOS times.
PowerShell is not just a command-line shell – it is powerful scripting environment. You can write own "methods" (cmdlets), use third-party cmdlets libraries.
PowerShell comes with Windows PowerShell ICE editor in which you can write/run/debug your scripts:

And finally, PowerShell has a default blue background and cmd has a default black background. Just kidding.

Running PowerShell script stored in a file

By convenience PowerShell scripts are stored in files with ps1 eextension. Create simple script and save it to test.ps1 file:

# this is comment
Write-Output "Hello from PowerShell script file"

In explorer navigate to directory with test.ps1 file and double-click on it. Most probably test.ps1 will be opened as simple text file in notepad.exe. This is because by default ps1 files are associated with notepad.exe. This saves users from accidentally running malware scripts (a common problem of bat and cmd scripts).
One of the easiest ways to run a script is to launch PowerShell console, navigate to directory with test.ps1 file and type .\test.ps1 then Enter:

PS C:\> cd C:\tmp
PS C:\tmp> .\test.ps1
File C:\tmp\test.ps1 cannot be loaded because the execution of scripts is disabled on this system. Please see "get-help about_signing" for more details.
At line:1 char:11
+ .\test.ps1 <<<<
    + CategoryInfo          : NotSpecified: (:) [], PSSecurityException
    + FullyQualifiedErrorId : RuntimeException
PS C:\tmp>

One more anti-malware protection: by default you cannot run PowerShell files! Let’s fix this. Run PowerShell as administrator and type following command:

PS C:\Windows\system32> Set-ExecutionPolicy RemoteSigned -Force

Above line changes PowerShell scripts execution policy from Restricted to RemoteSigned – allow run all locally created scripts. More about PowerShell execution policy you can read on the Internet.
Now you can finally run test.ps1 script:

PS C:\tmp> .\test.ps1
Hello from PowerShell script file
PS C:\tmp>

Success! Yes, it took a bit of investigations and work to get things working.

PowerShell in action

Let’s see some examples of useful things you can do with PowerShell.
  • Find all Internet Explorer processes and terminate them:
Get-Process iexplore | Stop-Process
  • Restart TeamCity Server service:
PS C:\tmp> Get-Service TeamCity | Restart-Service
  • Create Windows shortcut, for example to notepad.exe:
$shell = New-Object -COM WScript.Shell
$shortcut = $shell.CreateShortcut("C:\tmp\ShortCutToNotepad.lnk")
$shortcut.TargetPath = "%windir%\system32\notepad.exe"
$shortcut.Save()
  • Get 10 newest records from System event log:
Get-EventLog system -newest 10
  • List Windows Registry Key containing auto-start applications:
cd HKLM:\Software\Microsoft\Windows\CurrentVersion\Run
Get-ItemProperty .


Using C# code in PowerShell script

Being .NET based technology PowerShell gives you full power of .NET platform. Your script needs generate random number? No problem – just use System.Random class. Actually PowerShell allows you to write scripts almost in C# language, so you can implement arbitrary custom logic in PowerShell!

PS C:\tmp> $rnd = New-Object System.Random
PS C:\tmp> $i = $rnd.Next(10)
PS C:\tmp> [System.Console]::WriteLine("And the answer is ... {0}", $i)
And the answer is ... 7
PS C:\tmp>

Incredible, right?
What time is it now?

Get-Date
Tuesday, October 01, 2013 1:17:12 PM
[System.DateTime]::Now
Tuesday, October 01, 2013 1:17:22 PM

You can even use Windows Forms .NET API to create and show graphical dialogs directly from PowerShell:

Add-Type -AssemblyName System.Windows.Forms
$form = New-Object Windows.Forms.Form
$form.Size = New-Object Drawing.Size @(200,100)
$form.StartPosition = "CenterScreen"
$form.Text = "Hello!!!"
$form.ShowDialog()

Want use Amazon .NET API to start a new micro instance? Download Amazon .NET API and copy AWSSDK.dll to C:\tmp directory. Then write a script:

[Reflection.Assembly]::LoadFile("C:\tmp\AWSSDK.dll")
$AWSAccessKey = "<your access key>"
$AWSSecretKey = "<your secret key>"
$ec2 = [Amazon.AWSClientFactory]::CreateAmazonEC2Client($AWSAccessKey, $AWSSecretKey)
$request = New-Object Amazon.EC2.Model.RunInstancesRequest
$request.ImageId = $amiImageId
$request.InstanceType = 't1.micro'

$ec2.RunInstances($request)

Again, all above code is very similar to C# code. With time you’ll get used that all variable names in PowerShell must start from dollar sign ($), instead of new keyword you’ve to write New-Object, square brackets double colons static methods invocation syntax and other peculiarities.

PowerShell Remoting

PowerShell would not be so popular without PowerShell Remoting feature which was introduces in PowerShell 2.0. It allows you run PowerShell scripts on remote machines alike you run them on your local machine. It is very similar to Linux Secure Shell protocol (SSH). As everything potentially dangerous in PowerShell, PowerShell Remoting is not enabled by default. And you cannot enable it remotely :). So you must have RDP access to machine on which you want enable PowerShell remoting and be a member of Administrator group on that machine. You can Google how to enable PowerShell remoting. Here is short instruction:
  • RDP to TEST-PC and start elevated PowerShell console (Run as Administrator).
PS C:\Users\roman.turovskyy> Enable-PSRemoting -Force
WinRM has been updated to receive requests.
WinRM service started.
  • Return back to your working machine and start elevated PowerShell console. Enter following:
PS C:\windows\system32> cd WSMan:\localhost\Client
PS WSMan:\localhost\Client> Set-Item .\TrustedHosts * -Force
  • This will allow you to connect to any machine. Now enable PowerShell remoting on your machine and establish remote session with own machine (localhost):
PS C:\tmp> Enable-PSRemoting -Force
PS C:\tmp> Enter-PSSession -ComputerName localhost
[localhost]: PS C:\Users\roman.turovskyy\Documents>
  • Above line indicates that PowerShell remoting on your machine is properly configured. Now establish remote session with TEST-PC machine:
[localhost]: PS C:\Users\roman.turovskyy\Documents> Exit-PSSession
PS C:\tmp> Enter-PSSession -ComputerName TEST-PC
[TEST-PC]: PS C:\Users\roman.turovskyy\Documents>

Now you can run PowerShell commands on TEST-PC! Ensure that this is really TEST-PC by reading computer name from environment variable:

[TEST-PC]: PS Env:\> echo $env:COMPUTERNAME
TEST-PC

With PowerShell remoting you can run your script on many machines. For example you have a task to stop Windows Time service on many machines. Once you have PowerShell remoting configured on these machines you can use following script:

@('TEST-PC ', 'TEST-PC2') | foreach { Invoke-Command -ComputerName $_ -ScriptBlock { Get-Service W32Time | Stop-Service } }

$_ refers to current iteration value in foreach statement. You can also store enter-separated list on machines in a file:

PS C:\tmp> Get-Content machines.txt | foreach { Invoke-Command $_ { $env:COMPUTERNAME  } }
TEST-PC
TEST-PC1

Conclusion

PowerShell is very powerful shell scripting language. Being built on top of .NET it allows reusing existing .NET classes and writing arbitrary custom logic almost like in C# language. PowerShell remoting enables remote scriptable control over computes within your network. On our project we use PowerShell to deploy products on many machines within LAN. If you are not using PowerShell yet – take a look at it!

2 comments:

  1. Thanks for simple and "powerful" article! I have never used PowerShell before but feel like ready to try it now!

    ReplyDelete
  2. Did you know, this stuff as well can be used for Cloud environment Office-365, Azure, etc. FYI.

    ReplyDelete

Note: Only a member of this blog may post a comment.