Showing posts with label Scripting Techniques. Show all posts
Showing posts with label Scripting Techniques. Show all posts

May 13, 2015

Converting to the local time

To convert a date/time string from GMT (or any other time zone)

[DateTime]::Parse('2015-05-08T15:17:52Z')

Source : #PSTip Converting to the local time

August 12, 2014

Don’t Count, Measure

How often do you use the  Count property of an object to get the number of items in it ?

$SomeArray.count

The problem is that if you try to get the  Count property on a null object or on a object that is not a collection or an array it will throw an exception.

The solution is to use the Measure-Object cmdlet :

($SomeArray | measure-object).count

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.

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"

February 25, 2013

Using Out-Gridview to select objects

If you use the –Passthru parameter with Out-GridView,you will be able to select one or more row, click OK and send the result to the pipeline.

For example, if you wish to restart one or more services, try :

get-service | out-gridview -PassThru | restart-service -Verbose

February 22, 2013

How to test for Elevated Privilege

$currentPrincipal = New-Object System.Security.Principal.WindowsPrincipal([System.Security.Principal.WindowsIdentity]::GetCurrent())
$administratorsRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator
if (!$currentPrincipal.IsInRole($administratorsRole)) {
    Throw "This script must be run in an elevated session."
}

February 13, 2013

How to add –whatif support to a function

 

Function Get-HelloWorld {
    [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact="Low")]
    param()
    $message="Hello world"
    if ($PSCmdlet.ShouldProcess($message,"Write-host")) {
        write-host $message
    }
  }

Get-HelloWorld -whatif will produce :

What if: Performing operation "Write-host" on Target "Hello world".

As a bonus, you will also be able to use the -confirm and –verbose parameter

August 30, 2012

How to use expressions with Select-Object

You can use expressions with a select command to create custom calculated columns. Simply create an Hash table with 2 objects : name, expression.

Here’s a sample :

Get-ChildItem C:\ | Select-Object Name, length, @{Name="Kbytes";Expression={$_.Length / 1Kb}}

August 16, 2012

How to do a PAUSE

In the good old days, with cmd.exe batchs you can easily add a PAUSE in your script to allow you press a key to continue.

In Powershell you have to use the ReadKey() method. The only bug is that you can’t use ReadKey() if your not in the console. That could be a problem if you use PowerGUI or PowerShellISE to develop and run your scripts.

Here’s a little function that will do the trick :

#.SYNOPSIS 
# Emulation of PAUSE command (cmd)
#.DESCRIPTION
# Author : Jeffrey Snover [MSFT]
#.LINK
# http://blogs.msdn.com/b/powershell/archive/2007/02/25/pause.aspx
function Pause ([string]$Message="[Pause] Press Enter key to continue ...")
{
	if (((Get-Host).Name) -eq 'ConsoleHost') {
		Write-Host -NoNewLine $Message
		$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
		Write-Host ""
	}
	Else {
	Read-Host -Prompt $message
	}
}

Thanks to Jeffrey Snover for this code.

April 2, 2012

Top 5 tips for running external commands in Powershell

Running an external command in powershell looks pretty easy, to run notepad just do :

notepad.exe

Things get a little be more complicated when you add many arguments with spaces and quotes, something like the following just wont work :

Winrm set winrm/config/listener?Transport=HTTP+Address=* @{Enabled="false"}

Why is that ?

Powershell is a programming language, it has to evaluate (interpret) every line in a script as an expression. The preceding line is not a valid Powershell expression. Each arguments must be treated as an object (a String in this case), So a valid expression would be :

&Winrm "set" "winrm/config/listener?Transport=HTTP+Address=*" "@{Enabled=`"false`"}"

As you can see this can rapidly become a nightmare when you add variables and path to that call. Really not easy to debug. This is why I wrote my top 5 tips for running external commands in Powershell.

Tip #1 : Use the ampersand call operator (&)

Using the call operator (&) will help Powershell to evaluate the line as a command and not as an object. For example it will allow the following command to start Internet Explorer :

&"C:\Program Files\Internet Explorer\iexplore.exe"

Tip #2 : Do not put all arguments in one string

Each string will be treated one argument. Put each argument in a different string et separate them with a space or even better put them in an array like this :

$CallArgs = @("set",
		"winrm/config/listener?Transport=HTTP+Address=*",
		"@{Enabled=`"false`"}"
		)
&WinRM $CallArgs

Tip #3 : Don’t forget to escape each quote (")

If you want a quote to appear in an argument you must escape it like this : `"

Tip #4 : Use EchoArgs.exe for debugging

Debugging the call operator can be pretty hard simply because you can’t see what is really passed as arguments to the external command by Powershel. EchoArgs.exe is a great little tool that will simply display the arguments and the command line used to call the program. So the following command :

&.\echoargs.exe winrm "set" "winrm/config/listener?Transport=HTTP+Address=*" "@{Enabled=`"false`"}"

Will produce :

Arg 0 is <winrm>
Arg 1 is <set>
Arg 2 is <winrm/config/listener?Transport=HTTP+Address=*>
Arg 3 is <@{Enabled=false}>

Command line:
"I:\Mes Documents UL\PoSH\EchoArgs.exe"  winrm set winrm/config/listener?Transport=HTTP+Address=* @{Enabled="false"}

EchoArgs.exe is part of the Powershell Community Extensions (PSCX). You should download the version 2.1 (still in beta) to get the full comand line displayed. The Arguments (arg 0, arg 1, …) displayed by EchoArgs.exe seems to have been normalized (quotes have bnne strip), so the full command line is very helpfull.

Tip #5 : Use dot-sourcing

Don’t forget to use Powershell dot-source syntax (.\) when you call an external command in the current directory :

&.\echoargs.exe

November 15, 2011

How to use Write-Verbose in a script ?

This one is quite easy, simply add a write-verbose command in your script :

write-verbose "The script is running !"

Then launch your script with the –verbose parameter :

.\myscript.ps1 -verbose
Explanation :

By default write-verbose output is not display because the default value of $VerbosePreference is set to SlientlyContinue. By adding the –verbose parameter, the value of $VerbosePreference is set to Continue (in the context of the script). 

November 4, 2011

How to replace values in a string

Sometimes you a have a string like ”Cannot find file $1” where you want to replace the $1 by a value (ex: myfile.txt). Sure you can use the –replace operator but there is a much more elegant solution : the –format operator.

$ErrMsg="Cannot find file {0}" -f 'myfile.txt'

$ErrMsg will output :
Cannot find file myfile.txt

You can even use the –format in a multi-value, multi-line way :

$ErrMsg=@"
Filename : {0}
Filepath : {1}
Size     : {2:N} MB
"@ -f 'Myfile.txt','C:\temp',(1234567/1MB)

$ErrMsg will output :
Filename : Myfile.txt
Filepath : C:\temp
Size     : 1,18 MB

How to create a multiline string

 

$myString = @"
This is the first line
This the second line
This is the third line
"@

Note : the @” and the ”@ must be the last symbols on the line.

December 9, 2010

How to display a Yes/No message box ?

There isn’t a msgbox or an input function in Powershell, but you can directly call Windows.Forms.MessageBox  to do it. Thansks to Bart De Smet for his tutorial about that.

[reflection.assembly]::Load("System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089") | out-null

#.SYNOPSIS 
# Display a confrimation dialog
# Return$True if the user click 'Yes'
#.LINK
# http://bartdesmet.net/blogs/bart/archive/2006/09/16/4429.aspx
function ConfirmMessageBox {
	param(
		[parameter(
		Mandatory=$False)]
		[String]$WinTitle='PowerShell Script',
		[parameter(
		Mandatory=$False)]		
		$MsgText='Do you really want to continue ?'
	)
	$result = [Windows.Forms.MessageBox]::Show($MsgText, $WinTitle, [Windows.Forms.MessageBoxButtons]::YesNo, [Windows.Forms.MessageBoxIcon]::Question)
	If ($result -eq [Windows.Forms.DialogResult]::Yes) {
		Return $true
	}
	Else {
		Return $false
	}
}

August 13, 2010

How to define a function with ‘ByRef’ arguments ?

In VBscript you can define a function using a ByRef argument like this :

Function CommentLine (ByRef arg)

In PowerShell you have to use the [ref] type to pass the reference of a variable instead of its value.

Here’s a example of how to define and call a function using a [ref] argument :

function CommentLine([ref]$arg) {
	$arg.value = "# " + $arg.value
}

$Line="This should be a comment"
CommentLine ([ref]$Line)
$line

Don’t forget your passing the reference of the object, you have to use the value property of the reference to access the variable or object itself.

August 11, 2010

How to load an assembly

When you want to access the class from an assembly you have to load it. For those of you familiar with .net development (Visual Studio) it is the equivalent of adding a reference to an assembly in you project and adding an import statement like this :

imports Microsoft.UpdateServices.Administration

In powershell you do it this way :

[reflection.assembly]::Load("Microsoft.UpdateServices.Administration, Version=3.1.6001.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35")

Please note that you should use the complete assembly display name. You can get all that info (name, version, token) from the GAC (c:\windows\assembly) when you display the properties of an assembly.

You should not use the LoadWithPartialName method, it is obsolete. Read Suzanne Cook's .NET CLR Notes to understand why.

April 20, 2010

How do I call a script within a script?

Normally when you call a script it launch in a new scope. The trick is to use “dot sourcing”, this will force the script to launch in the same scope.

So in your script you can :

. .\Myfunctions-Library.ps1