Applications developed using the .NET Framework aren't compiled in the true sense of the word.   Writing an application in Visual Basic .NET, Visual C++ .NET or Visual C# .NET using Visual Studio doesn't result in machine code being generated.   Instead, the result is Microsoft Intermediate Language (MSIL) code - a platform independent "virtual" machine code, which runs on top of the .NET Framework Common Language Runtime (CLR).

The .NET Framework exposes numerous data types, including simple types, such as Boolean, Char, Int32 and Int64 as well as hundreds of compound data types, such as System.Array, System.Buffer and System.Tuple.

When compiling a Visual Studio application (into MSIL), the compiler performs rigorous type checking, meaning that the resultant code will be "type safe" at the point of execution.

As already discussed in the , PowerShell sits directly on top of the .NET Framework, and is therefore able to use any of the .NET data types.   However, in contrast to applications written using Visual Studio, PowerShell is an interpreted language, meaning that the MSIL code is generated on-the-fly, at run-time.   Therefore, type checking cannot be carried out with the same level of rigor, resulting in code that may not be type safe.

PowerShell has been designed to achieve two conflicting goals.  One the one hand, it needs to provide an interactive shell which can quickly return "rich" results to both novices and experts alike.  On the other hand, it needs to provide a robust scripting environment for configuring, controlling and monitoring enterprise class systems, such as Microsoft Active Directory, Microsoft Azure, Microsoft Exchange, Microsoft Hyper-V, Microsoft SQL Server and Microsoft System Center.

In order to achieve the first of these goals, PowerShell allows both dynamic typing and loose typing.   This doesn't cause concern when using PowerShell as an interactive shell, but it's not good news for production, server-side scripts.

As mentioned above, PowerShell allows dynamic typing to be used.  That is, a variable can assume a data type at run-time, and subsequently change type during execution.   When assigning a value to a variable, without specifying a data type, PowerShell will try to select the most appropriate data type for you.  If you then perform an operation that requires a type change, PowerShell will oblige.  Consider the following example:

PS C:\Users\JohnDoe> $i = 2147483647; PS C:\Users\JohnDoe> $i.GetType().Name; Int32 PS C:\Users\JohnDoe> $i++; PS C:\Users\JohnDoe> $i.GetType().Name; Double PS C:\Users\JohnDoe>

As you can see, the variable $i starts off as an Int32 (a 32-bit signed integer), but then becomes a Double when its value exceeds the capabilities of an Int32.  It's tempting to think that this behaviour is desirable or even convenient.  However, automatic type selection and dynamic type changes can quickly lead to unpredictable results, which isn't good for server-side production scripts, where data type overflow or mismatches must be caught, logged and possibly alerted.

A far more robust approach is to assign static data types to variables.   This means that issues such as integer overflows will raise exceptions, but at least the script will maintain values that are within bounds and can therefore be safely passed to functions and class methods that expect the given data type.  To assign a static data type to a variable, simply specify the data type name in square brackets, for example:

PS C:\Users\JohnDoe> [Int32] $i = 2147483647; PS C:\Users\JohnDoe> $i.GetType().Name; Int32 PS C:\Users\JohnDoe> $i++; Cannot convert value "2147483648" to type "System.Int32". Error: "Value was either too large or too small for an Int32." At line:1 char:1 + $i++ + ~~~~ + CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException + FullyQualifiedErrorId : RuntimeException PS C:\Users\JohnDoe>

As you can see, the integer overflow caused an to be raised, however, this can be caught using an exception handler (covered later on).

PowerShell is also loosely typed.  That is, you can perform operations such as add a string value to an integer value, or add an integer value to a string value.  Again, PowerShell will try to give you the result you desire.  Consider the following examples:

PS C:\Users\JohnDoe> $i = '1'; PS C:\Users\JohnDoe> $i.GetType().Name; String PS C:\Users\JohnDoe> $j = 23; PS C:\Users\JohnDoe> $j.GetType().Name; Int32 PS C:\Users\JohnDoe> $i + $j; 123
PS C:\Users\JohnDoe> $i = 1; PS C:\Users\JohnDoe> $i.GetType().Name; Int32 PS C:\Users\JohnDoe> $j = '23'; PS C:\Users\JohnDoe> $j.GetType().Name; String PS C:\Users\JohnDoe> $i + $j; 24 PS C:\Users\JohnDoe>

This behaviour is known as data type coercion.  And again, although convenient, this too can cause unpredictable results, especially if the values being coerced have been sourced from human input or from a data feed of some kind.

Again, a more robust approach is possible.  In this case, it's type casting, where the developer uses a casting method (see , below) to specify exactly what data type(s) are expected at the point of carrying out the data operation.  For example, you may have read a numeric text string from a data file and need to add the value to a running total.   In this instance, the running total will be a known integer data type and the data being loaded from the file will be a string value.  Using a casting method will cause an to be raised if the value can't be represented as an integer.  This can be seen below:

PS C:\Users\JohnDoe> [Int32] $intRunningTotal = 0; # Set static type and initialise. PS C:\Users\JohnDoe> [String] $strDataLoadedFromFile = 'abc123'; # Simulated value from file (an invalid numeric value). PS C:\Users\JohnDoe> $intRunningTotal += [Convert]::ToInt32( $strDataLoadedFromFile ); # Data operation with casting. Exception calling "ToInt32" with "1" argument(s): "Input string was not in a correct format." At line:1 char:1 + $intRunningTotal += [Convert]::ToInt32( $strDataLoadedFromFile ); # ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : FormatException PS C:\Users\JohnDoe>

However, with valid numeric input data, the casting operation succeeds:

PS C:\Users\JohnDoe> [Int32] $intRunningTotal = 0; # Set static type and initialise. PS C:\Users\JohnDoe> [String] $strDataLoadedFromFile = '123'; # Simulated value from file (a valid numeric value). PS C:\Users\JohnDoe> $intRunningTotal += [Convert]::ToInt32( $strDataLoadedFromFile ); # Data operation with casting. PS C:\Users\JohnDoe> $intRunningTotal; 123 PS C:\Users\JohnDoe>

In addition to static data type assignment and type casting, PowerShell's strict mode can also be used to further harden server-side scripts.  Strict mode is enabled using the Set-StrictMode cmdlet.   There are currently two versions of strict mode, unsurprisingly, version one and version two.   Enabling strict mode version two enforces two important features:

This capability is similar to Perl's use strict; and Python's use strict "vars"; directives.  An example of PowerShell's behaviour before and after strict mode has been enabled can be seen below:

PS C:\Users\JohnDoe> [String] $foo = 'bar'; PS C:\Users\JohnDoe> $foo.GetType().Name; String PS C:\Users\JohnDoe> $foo.NonExistentProperty;    # Referencing a property that doesn't exist goes unnoticed. PS C:\Users\JohnDoe>

Once again, this behaviour can lead to unpredictable results, for example, you could unknowingly continue to reference a class property that has been made obselete.  However, with strict mode enabled, an is raised:

PS C:\Users\JohnDoe> Set-StrictMode -Version 2;    # Enable strict mode. PS C:\Users\JohnDoe> [String] $foo = 'bar'; PS C:\Users\JohnDoe> $foo.GetType().Name; String PS C:\Users\JohnDoe> $foo.NonExistentProperty; The property 'NonExistentProperty' cannot be found on this object. Verify that the property exists. At line:1 char:1 + $foo.NonExistentProperty; + ~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], PropertyNotFoundException + FullyQualifiedErrorId : PropertyNotFoundStrict PS C:\Users\JohnDoe>

The .NET Framework (and therefore PowerShell) provides the following scalar (single value) data types:

Name Alternative Name Description Minimum Value Maximum Value
String ASCII or unicode string.

Strings can be encapsulated in single or double quotes, with single quotes being the preferred option.  Double-quotes have three uses:
  • Causes embedded variables to be expanded;
  • Allows single quotes to be processed without escaping them;
  • Causes embedded to be processed.
An empty string, i.e.: '' 2,147,483,647 bytes
Char Unicode 16-bit character. [Char] 0x0000 [Char] 0xffff
Byte An 8-bit unsigned character. 0 255
Int32 Int 32-bit signed integer. -2,147,483,648 2,147,483,647
Int64 Long 64-bit signed integer. -9,223,372,036,854,775,808 9,223,372,036,854,775,807
Boolean Bool Boolean true or false. $false (or zero) $true (or non-zero)
Decimal A 128-bit decimal value. -79,228,162,514,264,337,593,543,950,335 79,228,162,514,264,337,593,543,950,335
Single Float Single-precision 32-bit floating point number. -3.402823 x 1038 3.402823 x 1038
Double Double-precision 64-bit floating point number. -1.79769313486232 x 10308 1.79769313486232 x 10308
DateTime Used to store and manipulate dates. 1st January 0001 @ 00:00:00hrs 31st December 9999 @ 23:59:59
PS C:\Users\JohnDoe> [Int32]::MinValue; -2147483648 PS C:\Users\JohnDoe> [Int32]::MaxValue; 2147483647 PS C:\Users\JohnDoe>

PowerShell arrays are based upon the .NET Framework System.Array data type.  This data type is capable of representing single and multi-dimensional arrays, with the arrays being able to contain identical or varying data types.

The following examples demonstrate simple single-dimension array operations:

# Create an empty array able to hold varying data types. [System.Array] $arrOneDimensionEmpty = @(); # Create a populated array containing varying data types. [System.Array] $arrOneDimensionPopulated = @( 1, 'abc', [Math]::PI ); # Create a populated array containing just integer values. [Int32[]] $arrOneDimensionPopulatedInteger = @( 5, 12, 0, 20, -10 ); # Obtain the length of a given array. $arrOneDimensionPopulatedInteger.Count; # Append an item to an array. $arrOneDimensionPopulatedInteger += 71; # Append multiple items to an array. $arrOneDimensionPopulatedInteger += @( 17, 9, 21 ); # Return the first element of an array. $arrOneDimensionPopulatedInteger[0]; # Return the last element of an array. $arrOneDimensionPopulatedInteger[-1]; # Return the penultimate element of an array. $arrOneDimensionPopulatedInteger[-2]; # Return specific elements of an array as another array (array "slicing"). # (Returns elements 0, 2 and 8). $arrOneDimensionPopulatedInteger[0, 2, 8]; # # Return a range of elements of an array as another array (array "slicing"). # (Returns elements 3 through 8). # $arrOneDimensionPopulatedInteger[3..8]; # # Return a combination of specific elements and a range of elements as another array (array "slicing"). # (Returns elements 0 and 2, 3 through 8 and 10). # $arrOneDimensionPopulatedInteger[0,2+3..8+10]; # # Join an array. # @( '192', '168', '0', '1' ) -join '.'; # The result being a string value of ''. # # Use the .NET IndexOf() and LastIndexOf() methods to determine the index of a the first/last # instance of a given array value. # $arrNumbers = @( 1, 2, 3, 4, 5, 4, 3, 2, 1 ); [System.Array]::IndexOf( $arrNumbers, 3 ); # Returns 2. [System.Array]::LastIndexOf( $arrNumbers, 3 ); # Returns 6.

Multi-dimensional arrays are defined and manipulated as follows:

# Create a simple multi-dimension array. [System.Array] $arrMultiDimensionArray = @( ('0:0', '0:1', '0:2'), ('1:0', '1:1', '1:2'), ('2:0', '2:1', '2:2') ); # Return the first sub-array. $arrMultiDimensionArray[0]; # Return the last sub-array. $arrMultiDimensionArray[-1]; # Return the first element of the third sub-array. $arrMultiDimensionArray[2][0]; # Append a fourth sub-array (note the preceding comma). $arrMultiDimensionArray += ,@( '3:0', '3:1', '3:2' );

Like most scripting languages, PowerShell supports hash tables (also known as associative arrays or dictionary objects).   A hash table lets you associate a given key value with a given data value.  Data can then be retrieved from the hash table using the key values.

The following example demonstrates the creation of a simple hash table:

[HashTable] $objHash = @{ 'Apple' = 'Fruit'; 'Steak' = 'Meat'; 'Carrot' = 'Vegetable' }

Values can then be retrieved using their key values, for example:

PS C:\Users\JohnDoe> $objHash.Item('Apple'); # Verbose method. Fruit PS C:\Users\JohnDoe> $objHash['Steak']; # Shorthand method. Meat PS C:\Users\JohnDoe>

If the hash table doesn't contain a data value for the given key, the returned value will be $null.

PS C:\Users\JohnDoe> $objHash['Pomegranate'] -eq $null; True PS C:\Users\JohnDoe>

To add a value to a hash table, or amend an existing value, simply do the following:

PS C:\Users\JohnDoe> $objHash.Item('Vinegar') = 'Condiment'; # Verbose method. PS C:\Users\JohnDoe> $objHash['Coffee'] = 'Drink'; # Shorthand method. PS C:\Users\JohnDoe>

To remove a value from a hash table, use the Remove method:

PS C:\Users\JohnDoe> $objHash.Remove('Steak'); PS C:\Users\JohnDoe>

PowerShell's behaviour, once again, comes into play when dealing with hash tables.  However, you can quite easily enforce static data types for your hash values, for example:

[HashTable] $objPlannetMass = @{ 'Mercury' = [Double] 3.301E+23; 'Venus' = [Double] 4.867E+24; 'Earth' = [Double] 5.972E+24; 'Mars' = [Double] 6.417E+23; 'Jupiter' = [Double] 1.899E+27; 'Saturn' = [Double] 5.685E+26; 'Uranus' = [Double] 8.682E+25; 'Neptune' = [Double] 1.024E+26; 'Pluto' = [Double] 1.471E+22 }

The Count property can be used to return the number of entries in a hash table:

PS C:\Users\JohnDoe> $objHash.Count; 4 PS C:\Users\JohnDoe>

Use the ContainsKey() and ContainsValue() methods to determine if the given key or value exists in the hash table:

PS C:\Users\JohnDoe> $objHash.ContainsKey('Banana'); False PS C:\Users\JohnDoe> $objHash.ContainsKey('Apple'); True PS C:\Users\JohnDoe> $objHash.ContainsValue('Machine'); False PS C:\Users\JohnDoe> $objHash.ContainsValue('Drink'); True PS C:\Users\JohnDoe>

To return the entire list of keys as an array, or the entire list of data values as an array, use the Keys or Values properties:

PS C:\Users\JohnDoe> $objHash.Keys; Carrot Apple Coffee Vinegar PS C:\Users\JohnDoe> $objHash.Values; Vegetable Fruit Drink Condiment PS C:\Users\JohnDoe>

Hash tables cannot be natively sorted.  However, we can use the GetEnumerator() method to cause the hash to be "pipelined".  This makes it possible to invoke the cmdlet, for example:

PS C:\Users\JohnDoe> $objHash | Sort-Object -Property Name; # Sort operation has no effect. Name Value ---- ----- Carrot Vegetable Apple Fruit Coffee Drink Vinegar Condiment PS C:\Users\JohnDoe> $objHash.GetEnumerator() | Sort-Object -Property Name; # Sort is successful. Name Value ---- ----- Apple Fruit Carrot Vegetable Coffee Drink Vinegar Condiment PS C:\Users\JohnDoe>

Also, you can use the Keys property to programmatically step through the contents of a hash, for example:

PS C:\Users\JohnDoe> foreach ( $strKey in $objHash.Keys ) { Write-Host -Object ('>{0}<' -f $strKey ); } >Carrot< >Apple< >Coffee< >Vinegar< PS C:\Users\JohnDoe>

To clear a hash table completely, use the Clear method:

PS C:\Users\JohnDoe> $objHash.Clear(); PS C:\Users\JohnDoe>

Finally, you can use a hash table to "splat" parameters into a function or cmdlet, for example:

PS C:\Users\JohnDoe> $splat = @{ 'LocalPath' = 'S:'; 'RemotePath' = '\\server\share'; 'UserName' = 'JohnDoe'; 'Password' = 'foobar'; } PS C:\Users\JohnDoe> New-SmbMapping @splat; Status Local Path Remote Path ------ ---------- ----------- OK S: \\server\share PS C:\Users\JohnDoe>

As mentioned in section , PowerShell natively supports XML (eXtensible Markup Language).  XML encoded data can be manipulated directly using the XML data type.   In addition, the ConvertTo-Xml cmdlet can be used to generate XML output and the Select-Xml cmdlet can be used to search for values within XML data.

PS C:\Users\JohnDoe> [XML] $objXML = '<GrandParentNode>GrandParentValue<ParentNode>ParentValue<ChildNode>ChildValue</ChildNode></ParentNode></GrandParentNode>'; PS C:\Users\JohnDoe> $objXML; GrandParentNode --------------- GrandParentNode PS C:\Users\JohnDoe> $objXML.GrandParentNode; #text ParentNode ----- ---------- GrandParentValue ParentNode PS C:\Users\JohnDoe> $objXML.GrandParentNode.ParentNode; #text ChildNode ----- --------- ParentValue ChildValue PS C:\Users\JohnDoe> $objXML.GrandParentNode.ParentNode.ChildNode; ChildValue PS C:\Users\JohnDoe>

PowerShell uses the well-known concept of scopes to protect variables, functions and other language components from inadvertent modification.  When defining a variable, the developer can specify a scope to control the visibility of the variable throughout the script.  There are four scopes: Global, Script, Local and Private.

The Global scope is created when the PowerShell process starts.   Any variables created at the interactive shell prompt that aren't created as Private automatically belong to the Global scope.   Any scripts that execute within the shell can subsequently access these variables, as long as they specify the Global scope in the variable reference, for example: $Global:foo.

Once a script is executing, any variables created in the script body, that is, not in a function or procedure, automatically belong to the Script scope, unless the developer chooses to make them Private or Global.

The Local scope behaves differently.   When at the interactive shell prompt, the Local scope and the Global scope are synonymous.  When in the body of a script, the Local scope and the Script scope are synonymous.  However, when in a function or procedure, the Local scope is its own entity, that is, a function or procedure can access the Global, Script and Local scopes.

Assigning a variable to the Private scope prevents it from being visible to child scopes, for example, a variable created as Private in the script body will not be visible from within a function.

PowerShell Process (POWERSHELL.EXE)
Default Scope:Global
Visible Scopes : Global (Local = Global)

PowerShell Script
Default Scope : Script
Visible Scopes : Global & Script (Local = Script)

Default Scope : Local
Visible Scopes:Global, Script & Local

It is strongly recommended that you do not reference Global or Script variables directly from within a function or procedure.  Doing so prevents, or at least complicates, code re-use.   A better approach is to pass the the Global or Script values, or pointer references thereof, to the function or procedure as parameterised input.  The exception to this rule is when referencing PowerShell's own Global variables, such a $PID, which holds the process ID of the PowerShell process.

When a PowerShell script is executed, any functions, procedures or variables defined in the Global scope will persist in the Powershell process when the script exits.  For example, the following script defines two functions, one in the global scope, and one with no scope definition (it will assume the Script scope - see above).

function global:globalFunc() { 'This is a function called {0} in the global scope.' -f $MyInvocation.MyCommand; return; } function scriptFunc() { 'This is a function called {0} in the script scope.' -f $MyInvocation.MyCommand; return; } exit 0;

If we execute the script, the function globalFunc() will be available from the shell when the script exits.   However, the function scriptFunc() won't, for example:

PS C:\Users\JohnDoe> .\scopetest.ps1 PS C:\Users\JohnDoe> globalFunc This is a function called globalFunc in the global scope. PS C:\Users\JohnDoe> scriptFunc scriptFunc : The term 'scriptFunc' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. At line:1 char:1 + scriptFunc + ~~~~~~~~~~ + CategoryInfo : ObjectNotFound: (scriptFunc:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException PS C:\Users\JohnDoe>

However, there are times when it would be convenient for all functions, procedures and variables defined in a given script to persist when the script exits.  This can be achieved using Dot Sourcing, where we prefix the script name with a dot and a space, for example:

PS C:\Users\JohnDoe> . .\scopetest.ps1 PS C:\Users\JohnDoe> globalFunc This is a function called globalFunc in the global scope. PS C:\Users\JohnDoe> scriptFunc This is a function called scriptFunc in the script scope. PS C:\Users\JohnDoe>

Dot sourcing is particularly useful if you have a script "library" of useful administrative functions that aren't in a ; you can simply invoke the script using dot sourcing to make its functions available in your current shell.

PowerShell automatically creates a number of variables, which it uses to store various items of state data, some of which are detailed below:

Variable Description
$$ Holds the last command in the shell history; equivalent to (Get-History)[-1].
$? Flag indicating whether the previous command succeeded (see ).
$_ or $PSItem Holds the current object in the pipeline (similar to Perl's default variable).
$args Array containing the arguments passed to the script.
$Error Array containing the shell's error history.  Can be cleared by calling $Error.Clear().
$ForEach Enumerator for loops.
$HOME Contains the current user's Windows profile path (e.g.: C:\USERS\JOHNDOE).  File system cmdlets accept the tilde character (~) as shorthand.
$Host An object that represents the hosting application of the PowerShell process.  Useful for performing raw user-interface operations, for example, $host.UI.PromptForCredential('Dialogue Title', 'Prompt', 'username', '' );.
$input Contains all of the data passed to a cmdlet via the pipeline.  Introduced in PowerShell V3.0.
$LASTEXITCODE Contains the return code from the last executed Windows program.
$Matches Contains the results of the last comparison.
$MyInvocation Contains information about the script or function being executed.
$PID The process ID of the current PowerShell process.
$PROFILE Holds the path of the current PowerShell profile script.
$PSBoundParameters A System.Management.Automation.PSBoundParametersDictionary object that stores all parameters passed to the script or current function via the param() construct.  The variable behaves like a regular .
$PSCulture Holds the current system culture, for example, en-GB.  The system culture determines how dates and currencies, Etc., are displayed.
$PSCommandPath Holds the full path of the current PowerShell script.  See also $PSScriptRoot.  Introduced in PowerShell V3.0; the PowerShell V2.0 equivalent is $MyInvocation.MyCommand.Path;
$PSHOME Contains the path of the PowerShell installation.
$PSScriptRoot Holds the directory (folder) of the current PowerShell script.  Introduced in PowerShell V3.0; the PowerShell V2.0 equivalent is Split-Path -Path $MyInvocation.MyCommand.Path -Parent;
$PSUICulture Holds the current system user-interface (UI) culture, for example, en-US.  The UI culture determines the language that is used to display titles, prompts and messages.
$PSVersionTable Holds the PowerShell version, .NET Framework Common Language Runtime (CLR) version, Etc.
$PWD Holds the fully qualified path of the current directory.

In addition, PowerShell also provides the following constants:

Constant Description
$false Represents boolean false (zero).
$null An uninitialised, undefined or meaningless value.  Returned by some functions to indicate an error or no data.  Also commonly assigned to a variable before instantiation (e.g.: before creating an instance of an object class).
$true Represents boolean true (non-zero).
[Math]::E Euler's number.  Used in continuous growth calculations, such as when calculating compound interest or modelling biological processes.
[Math]::PI Pi - the ratio of a circle's circumference to its diameter.
KB, MB, GB, TB and PB Built-in numeric representations of kilobyte, megabyte, gigabyte, terrabyte and petabyte.  For example, 1PB = 1,125,899,906,842,624 (or 250).

Windows' environment variables can be accessed using the $Env: prefix, for example, $Env:SystemRoot or $Env:APPDATA.   Alternatively, the entire environment variable namespace can be accessed via the Env: PowerShell , for example:

PS C:\Users\JohnDoe> Set-Location -Path Env: PS Env:\> ls Name Value ---- ----- ALLUSERSPROFILE C:\ProgramData APPDATA C:\Users\JohnDoe\AppData\Roaming CommonProgramFiles C:\Program Files\Common Files CommonProgramFiles(x86) C:\Program Files (x86)\Common Files CommonProgramW6432 C:\Program Files\Common Files COMPUTERNAME WIN10X64VM ComSpec C:\Windows\system32\cmd.exe FPS_BROWSER_APP_PROFILE_STRING Internet Explorer FPS_BROWSER_USER_PROFILE_ST... Default HOMEDRIVE C: HOMEPATH \Users\JohnDoe LOCALAPPDATA C:\Users\JohnDoe\AppData\Local LOGONSERVER \\MicrosoftAccount NUMBER_OF_PROCESSORS 2 OS Windows_NT Path C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPo... PATHEXT .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.CPL PROCESSOR_ARCHITECTURE AMD64 PROCESSOR_IDENTIFIER Intel64 Family 6 Model 37 Stepping 5, GenuineIntel PROCESSOR_LEVEL 6 PROCESSOR_REVISION 2505 ProgramData C:\ProgramData ProgramFiles C:\Program Files ProgramFiles(x86) C:\Program Files (x86) ProgramW6432 C:\Program Files PSModulePath C:\Users\JohnDoe\Documents\WindowsPowerShell\Modules;C:\Program Files\WindowsPowerShell... PUBLIC C:\Users\Public SESSIONNAME Console SystemDrive C: SystemRoot C:\Windows TEMP C:\Users\JohnDoe\AppData\Local\Temp TMP C:\Users\JohnDoe\AppData\Local\Temp USERDOMAIN WIN10X64VM USERDOMAIN_ROAMINGPROFILE WIN10X64VM USERNAME JohnDoe USERPROFILE C:\Users\JohnDoe windir C:\Windows PS Env:\>

The .NET Framework Convert class contains numerous methods for performing type casting.  To view the methods exposed by this class, type the following:

PS C:\Users\JohnDoe> [Convert].GetMethods() | Select-Object -Property Name -Unique | Sort-Object -Property Name; Name ---- ChangeType Equals FromBase64CharArray FromBase64String GetHashCode GetType GetTypeCode IsDBNull ToBase64CharArray ToBase64String ToBoolean ToByte ToChar ToDateTime ToDecimal ToDouble ToInt16 ToInt32 ToInt64 ToSByte ToSingle ToString ToUInt16 ToUInt32 ToUInt64 PS C:\Users\JohnDoe>

PowerShell also has its own built-in type casting operator (the -as operator).  Unfortunately, this operator doesn't raise an exception when a type casting operation fails (it simply returns $null).  For example:

PS C:\Users\JohnDoe> [String] $foo = 'somestring'; PS C:\Users\JohnDoe> $bar = $foo -as [Int32]; # Illegal operation. PS C:\Users\JohnDoe> $bar -eq $null; True PS C:\Users\JohnDoe>

This isn't at all robust and, again, can lead to unpredictable results.  The preferred method is therefore to use the Convert class, as described in the section, above.

PowerShell V3.0, and above, supports a number of validation declarations that can be used when accepting parameter input or when initialising variables.  The declarations offload certain validation tasks to the PowerShell interpreter, therefore reducing complexity, whilst, at the same time, increasing readibilty and overall robustness.

Validation failures cause one of two types of to be raised, depending on where the declaration is used.  If the declaration is used whilst accepting parameter input, a ParameterBindingValidationException exception will be raised; if the declaration is used whilst declaring a variable, a ValidationMetadataException exception will be raised.

The table below describes the available validation declarations:

Declaration Meaning Example(s)
[ValidateCount(min, max)] Declares the number elements an array or hash table may have. [ValidateCount(5, 12)] [String[]] $meetingAttendees
[ValidateLength(min, max)] Declares the permitted length of a given string value. [ValidateLength(2, 40)] [String] $surname
[ValidateNotNull()] Declares that a value must not be $null.
[ValidateNotNull()] [Net.Sockets.TcpClient] $TCPClient
[ValidateNotNullOrEmpty()] Declares that a value must not be $null or empty.
[ValidateNotNullOrEmpty()] [System.IO.File] $filehandle
[ValidatePattern('<RegEx>')] Declares that a value must match a given . [ValidatePattern('^(?:(0\d{4})\s+)?(\d{6})$')] [String] $ukTelephoneNumber

[ValidatePattern('^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$')] [String] $soureIP
[ValidateRange(min, max)] Declares the permitted range of a given numeric value. [ValidateRange(0, 65535)] $portnumber
[ValidateScript({<script block>})] Declares a script block that must evaluate to $true when processing the given value.  The value to be tested is stored in the $_ variable. [ValidateScript({$_ -ge (Get-Date)})] [DateTime] $meetingDateTime
[ValidateSet(<set>)] Declares that the value must exist in the given set. [ValidateSet('tcp', 'udp', 'icmp')] [String] $protocol

For further information regarding the use of validation declarations whilst accepting parameter input, please refer to the section.