Friday, July 18, 2008

Getting Started with PowerShell

Recently I started listening to a podcast that focuses on PowerShell and it inspired me to learn more about it. PowerShell is a Bash-like environment that will play a huge part in new and future Microsoft software and sofware from other vendors. Currently VMWare and Citrix are both making there products PowerShell aware and Exchange 2007 and Windows Core will heavily rely upon PowerShell.

Being a fan of the commandline I really wanted to learn more about PowerShell and from having no programming or scripting knowledge heres what I picked up in the past 2 days.

Mostly I was interested in how it might be useful for Systems Administration and getting right down into the guts of a system.


  • PowerShell
  • Snap-ins
    • Quest.ActiveRoles.ADManagement

Powershell Basics

A list of the available commands can be seen by typing get-command at the powershell prompt.

More information and exampes can be found about each command by typing get-help get-command for example.

PowerShell uses tab completion, and also has the beefit of aliases. The aliases can be listed by typing get-alias. Once you are familier with aliases these can save a whole load of typing, for example:

get-process -name vm* | format-Table


gps -name vm* | ft

In the example above, you can see that I have piped the output of get-process into the format-table commandlet. Piping is very common in PowerShell and it will allow you to create som useful one-liners.

One other word on tab completion, if I wanted to know what options were available for say the get-content commandlet i would type get-content - and then press tab to cycle through the options. From there I would see an option called wait. Using this I could run a command very similar to tail -f in Linux. For example:

get-content -wait logfile.txt

This will display the logfile in the current directory and if any new events occur I will see them straight away. Cool!

Below are some really useful one-liners that I have found that will be useful in these situations. I'll list the basic commands that can be run, and then i'll expand upon those commands to help explain how the commands may be run on remote systems, how the output might be filtered and redirected to a file rather than the screen.


To start with I'm simply going to retrieve running processes.


Now i could filter the output by a particular service by specifying the name.

Get-Process -Name firefox

And taking it a step further I can display the output as a list rather than a table.

Get-Process -Name firefox | Format-list

Now I might also want to see what other fields are available (easier to do this one as a list).

Get-Process -Name firefox | Format-List -Property *

And once I have identified some useful fields I'll focus on just those.

Get-Process -Name firefox | Format-List -Property ProcessName,FileVersion

Once i have narrowed down what I want I can output the results to either screen, file or printer using the commands below.

Get-Process | Out-Host -Paging | Format-List

Get-Process | Out-File -FilePath C:\temp\processlist.txt

Get-Command | Out-File -FilePath c:\temp\output.txt -Width 2147483647

Get-Command Get-Command | Out-Printer -Name "Microsoft Office Document Image Writer"

I might also want to list the highest load processes. For this I pipe the results of get-process into a where-object variable.

gps | where-object {$_.WorkingSet -gt 10000000}

WMI in PowerShell

I can also do get same info using WMI classes as can be seen below. Here I look at processes on a remote host and filter the output.

I can get a full list of WMI classes available by using this command

Get-WmiObject -List

As can be seen from the output there are hundreds of WMI objects that can be queried.

And here I query the processes using WMI

Get-WmiObject -Class Win32_process -Namespace root/cimv2 -ComputerName .

And on a remote host.

Get-WmiObject -List -ComputerName

And then i might filter the output using:

Get-WmiObject -Class Win32_process -Namespace root/cimv2 -ComputerName | format-list name, commandline

After querying the WMI classes available I can enumerate quite alot of information as can be seen from the commands below.

Get-wmiobject –class win32_service | select-object [a-z]* | export-csv c:\service.csv

Get-wmiobject -class win32_logicaldisk

Get-WmiObject -Class Win32_process -Namespace root/cimv2 -ComputerName

Get-WmiObject -Class Win32_OperatingSystem -Namespace root/cimv2 -ComputerName .

Get-WmiObject -Class Win32_OperatingSystem -Namespace root/cimv2 -ComputerName

Get-WmiObject -Class Win32_PrinterSetting -Namespace root/cimv2 -ComputerName

Get-WmiObject -Class Win32_userAccount -Namespace root/cimv2 -ComputerName

Get-WmiObject -Class Win32_useraccount -Namespace root/cimv2 -ComputerName | format-list accounttype, name

Eventlogs in PowerShell

In the examples below I will demonstrate how to display an eventlog in PowerShell and then filter the results.

Get-eventlog -logname system

The results from the above query will likely be overwhelming. I'll use a few comands to cut down on the data.

get-eventlog -newest 10 -logname system

I can also use the use export to csv function.

Get-eventog -logname system | epcsv c:\system-events.csv

Or to filter or exclude on just certain events.

get-eventlog -newest 100 -logname system | where { $_.eventid -match "7036"}

get-eventlog -newest 100 -logname system | where { $_.eventid -notmatch "7036"}

get-eventlog -newest 100 -logname system | where {$_.entrytype -match "warning"}

Active Directory

With the free Active Directory add-in from Quest you can query many properties on objects within AD. Below are some examples.

get-qaduser -company * -sl 500 | select company | sort -property company | Get-Unique -asstring

This query would look at the company field in a users AD profile and return no more than 500 unique results. obviously this could be applied to any field.

To focus on just one AD user called John Smith I might use:

get-qaduser "John Smith"

To see all the AD fields available I would use the following command:

get-qaduser "John Smith" | select *

or for very verbose output try:

get-qaduser "John Smith" -includeallproperties | select *

Terminal Services

Now this I found very useful as I was always unable to query AD for a users terminal services path using traditional tools. But here I am able to filter just on those fields.

get-qaduser "John Smith" | format-list Tsp*

or o use wildards:

get-qaduser "John*" | Format-table name, tsp*

and then to output it to a file I might use:

get-qaduser "John*" | Format-table name, tsp* > c:\term-serv-prof.csv

Disks Information

A really important part of my job is to make sure there is adequate diskspace on many servers across many locations. I developed a one-liner at the bottom of this section that will query a list of servers and get the disk information for me.

I start by looking at my local disk to get familier with the commands.

Get-WmiObject -Class Win32_LogicalDisk -Filter "DriveType=3" -ComputerName .

To see all the disk properties available I use:

Get-WmiObject -Class Win32_LogicalDisk -Filter "DriveType=3" -ComputerName . | Select-Object -Property *

Before I continue, if i want to learn more about the properties I can pipe the WMI class into get-member. get-member is really useful and will tell me loads about any properties of the commands.

From here I can see what the methods are (things you can do) and what the properties are (things you can query).

Now Focus on just the ones I need.

Gwmi win32_logicaldisk -Filter "DriveType=3" -comp . | Select-Object -Property SystemName,Caption,VolumeName,Name,FreeSpace | format-table

Ah, it's slightly anoying that all my output didn't fit on the screen. after looking at the help (get-help format-table -full) I find there is an auto size switch.

Thats better.

So now I try using a list of servers to see how that goes. My list is just the server names in a text file. And I use the alias for Get-WmiObject.

Gwmi win32_logicaldisk –comp (gc c:\servers.txt)

That went well, so i build on it to examine them for disknames, partition, total size and free space and out put the results to a new file (Server-Disk.csv)

Gwmi win32_logicaldisk -Filter "DriveType=3" -comp (gc c:\servers.txt) | format-table -property SystemName, Caption, VolumeName, Size, Freespace | out-file -filepath c:\Server-Disk.csv

But thats not good enough, I now need to convert the disk sizes to GB. I try it first on my local drives.

Gwmi win32_logicaldisk -Filter "DriveType=3" -comp . | Select-Object -Property SystemName,Caption,VolumeName,Name,FreeSpace | ForEach-Object -Process {$_.FreeSpace = ($_.FreeSpace)/1024/1024/1024; $_} | format-table -auto

Cool, and now for the servers.

Gwmi win32_logicaldisk -Filter "DriveType=3" -comp (gc c:\servers.txt) | Select-Object -Property SystemName,Caption,VolumeName,Name,Size,FreeSpace | ForEach-Object -Process {$_.FreeSpace = ($_.FreeSpace)/1024/1024/1024; $_} | format-table | out-file -filepath c:\Disk-GB.csv

Now after testing this I found the command Export-CSV, I could have used that instead of out-file -filepath which may have been slightly better for formating. But I need to test that.


Below are a few commands that return details on the network card configuration.

Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE -ComputerName . | Format-list -Property *

Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=TRUE -ComputerName . | Format-Table -Property IPAddress

Get-WmiObject -Class Win32_NetworkAdapterConfiguration -Filter "IPEnabled=true and DHCPEnabled=true" -ComputerName .

This ones interesting, I am able to ping a remote host from another remote host.

Get-WmiObject -Class Win32_PingStatus -Filter "Address=''" -ComputerName | Format-Table -Property Address,ResponseTime,StatusCode -Autosize

Or I can ping a list of hosts from my PC.

"","" | ForEach-Object -Process {Get-WmiObject -Class Win32_PingStatus -Filter ("Address='" + $_ + "'") -ComputerName .} | Select-Object -Property Address,ResponseTime,StatusCode | format-table -auto

The same thing again but to local addresses in the 10.50.1.x range.

1..10| ForEach-Object -Process {Get-WmiObject -Class Win32_PingStatus -Filter ("Address='10.50.1." + $_ + "'") -ComputerName .} | Select-Object -Property Address,ResponseTime,StatusCode

To Create a share using PowerShell

(Get-WmiObject -List -ComputerName . | Where-Object -FilterScript {$_.Name -eq "Win32_Share"}).Create("C:\temp","NewShare",0,25,"temp folder share")


Now I'm a big fan of looking at the registry and PowerShell is not too shabby for using as a tool to do this.

Here are some basic queries.

Get-ItemProperty -Path Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

Get-ItemProperty -Path Registry::'HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\TypedURLs'

And now I use PowerShell to create a new key.

New-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run -Name Test -PropertyType String -Value C:\Windows

And delete the key.

Remove-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion -Name Test

One thing that I havent been able to get as a one-liner just yet is the modification of remote registries. But I am working on it. Also XP allowed me to create registry entries but Vista did not.


Fisnally, below arer some useful one-line that I thought i would include as it's handy to refer back to these at some stage.

Get-WmiObject -Class Win32_LogonSession -ComputerName . | Select-Object -Property *

Get-WmiObject -Class Win32_ComputerSystem -Property UserName -ComputerName .

Get-WmiObject -Class Win32_LocalTime -ComputerName . | Select-Object -Property [a-z]*

Examples of filters:

Get-WmiObject -Class Win32_SystemDriver | Where-Object -FilterScript {$ -eq "acpi"} | Format-list -property *

Get-WmiObject -Class Win32_SystemDriver | Where-Object -FilterScript {$_.State -eq "Running"} | Where-Object -FilterScript {$_.StartMode -eq "Manual"}

Getting the time from servers and outputting the results to a new file:

Get-Content servers.txt | %{$x = net time \\$_; $x[0];If($x[2].contains(”Local”)){$x[2]}} | Add-Content Servertime.txt

Listing open sessions on a remote server:

Get-TerminalSession -ComputerName server123 | format-list -property client, state, username

Searching through a file and outputting content to a new file:

Get-Content file.txt | select-string -pattern "admin*" | set-content admin-users.txt

Searching through a directory for content in a file:

Select-String -path "C:\Users\Lee\*.*" "admin"

To create a variable and then search it:

$wmi = get-wmi-object -list

$wmi | select-string -pattern "network" , "security"

Finally, thank you to all the guys from the PowerShell Community Forums who have helped me get started on Powershell.


Anonymous said...

Thanks Syn

Question, its seems like there unlimited ways the company network can upload my files, I view these actions on XML files, is it true?
If so, how do I put a stop to it?


NO AIM please

Anonymous said...

Your blog is so good.I'm your fan..goahead..You can access my blogs.We will share ideas together.

SynJunkie said...

Hey thanks man!

Anonymous said...

hi,You are welcome!.Dear,Can you suggest some websites,which can help me in programming-level with powershell.If you have any blog or website links,please reply.I will expect the reply in your blog comments...

Blog said...

Can you help me debug this powershell script.There are some erorrs and i cannot fix it.

write-host -foregroundcolor green
$args= `localhost`
if($args -eq "?")
{ "ReportDiskdriveconfiguration.ps1
gwmi win32_diskdrive -computer $args

SynJunkie said...

Blog, i'm not too sure what it is your trying to do.

I would suggest posting to the Powershell Community forums. You'll get a lot more people looking at the script and I can guarantee that they are alot better scripters than I am.


Anonymous said...

No need.I fixed the error.Thank you.