June 13, 2014

How to call a Powershell script from the command line and return an ERRORLEVEL

Sometimes you want to call a powershell script from the command line and get an ERRORLEVEL a the exit if an exception is throw in the script (ex: With a scheduled task).

In Powershell v2 you could do this :

powershell.exe -noprofile -command ". c:\test-exit.ps1 -erroraction stop;exit $LastExitCode"

Unfortunately this does not work anymore in Powershell v4 (always return ERRORLEVEL 0). It looks like the $LastExitCode is not define anymore for a script by Powershell (only for an external process).  But Powershell seems to now behave as expected and exit with an ERRORLEVEL=1 if any error occurs in your script. So now, the following code should works :

powershell.exe -noprofile -command ". c:\test-exit.ps1 -erroraction stop"

If you really want to check if your script was successful and return a special ERRORLEVEL, you should use the $? variable :

powershell.exe -noprofile -command ". c:\test-exit.ps1 -erroraction stop;if ($?){exit 0}else{exit 99}"

Don’t forget :

  • To use dote-sourcing, the small dot (.) in  front of the script name.
  • To use the –command parameter instead of the –file parameter. The two parameters behave differently.
  • To set the –erroraction stop. If you keep the default value of continue, your script will not stop and the ERRORLEVEL will not be set to 1.

May 2, 2014

Find all files modified after a date

To find all files modified after may 1st, 2014 :

Get-ChildItem .\ -recurse | Where-Object { ($_.PsIsContainer -eq $false) -and ($_.LastWriteTime -gt (get-date '2014-05-01')) } | select fullname,lastwritetime,length | Out-GridView

January 6, 2014

Loading (import) a module with arguments

You can use the –ArgumentList parameter of the import-module cmdlet to pass arguments when loading a module.

You should use a param bloc in your module to define your parameters :

param(
    [parameter(Position=0,Mandatory=$false)][boolean]$BeQuiet=$true,
    [parameter(Position=1,Mandatory=$false)][string]$URL
)

Then call the import-module cmdlet like this :

import-module .\myModule.psm1 -ArgumentList $True,'http://www.microsoft.com'

As may have already noticed, you can only supply values (no names) to –ArgumentList. So you should define you parameters carefully with the position argument.

October 29, 2013

How to export/pipe/copy results to Excel

Sometimes you just want to paste the result of a cmdlet into an Excel spreadsheet.

First you will probably try to copy the content from a Gridview :

get-process | out-gridview

But you will realise that the column’s headers are missing.

Why not try to pipe the result into the clip command and paste it in Excel :

get-process | clip

Well not so great, everything is in one big column (no delimiter).

Ok, let’s try to convertto-csv before doing the copy :

get-process | convertto-csv -Delimiter "`t" -NoTypeInformation | clip

Looks great … but if you happen to have any french characters, they will not appear in Excel.

So, my final solution is to re-encode the output in UTF8 by saving it to a file :

get-process | export-csv -Delimiter ";" -NoTypeInformation -force -path "$env:TEMP\export-csv.csv" -Encoding UTF8; start-process excel "$env:TEMP\export-csv.csv"

August 23, 2013

How to use write-verbose with an object

When you are debugging some times you want to use the write-verbose cmdlet to display an object or a collection.  It would have been really nice to be able to do :

write-verbose (Get-EventLog -LogName system -Newest 3 -EntryType Error)

unfortunately this will produce an error :

Write-Verbose : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'Message'. Specified method is not supported.

The write-verbose cmdlet only takes a string as a paramter, objects are not allowed.
The trick is to use the out-string cmdlet like this :

write-verbose (Get-EventLog -LogName system -Newest 3 -EntryType Error | Out-String)

You can event make it look a little bit prettier :

write-verbose (Get-EventLog -LogName system -Newest 5 -EntryType Error| format-list -Property TimeGenerated,Entrytype,EventID,Source,Message | out-string)

August 22, 2013

How to get all web sites Bindings with site names

Get-WebBinding can return the bindings for all your IIS web sites, but the name of the web site is not a property of the resulting object, you have to guess it from the ItemXpath property (not always usefull).

On the other hand Get-WebSite can return the Bindings and the web site name. But the Bindings property is a Microsoft.IIs.PowerShell.Framework.ConfigurationElement object witch must be expand to get to the Collection Property, witch must expand again to get to the BindingInformation. This is not an easy way to get to the BindingInformation and when you will get it you will have lost the site name associated.

My solution is to create a [pscustomobject] (Powershell v3 only) that will contain all the properties I need :

Foreach ($Site in get-website) { Foreach ($Bind in $Site.bindings.collection) {[pscustomobject]@{name=$Site.name;Protocol=$Bind.Protocol;Bindings=$Bind.BindingInformation}}}

August 1, 2013

Simple WMI Query

Here’s the basic ways to query WMI from Powershell.

Option 1 : With the class and a filter
Get-WmiObject -Class Win32_Directory -filter "Name='c:\\temp'"
Option 2 : With the full query
Get-WmiObject -Query "select * from Win32_Directory where Name='c:\\temp'"
More Info
  • You can also use the GUI tool wbemtest (builtin Windows)
  • You can also use the command line tool wmic (builtin Windows) 
    wmic path Win32_Directory where name='c:\\temp'
  • Backslachs (\) have to be escaped in your query (\\)