Windows PowerShell is a shell and scripting component of the Windows Management Framework, an automation/configuration management framework from Microsoft built on the .NET Framework. PowerShell is installed by default on all supported versions of Windows client and server operating systems since Windows 7 / Windows Server 2008 R2. Powershell can be updated at any time by downloading a later version of the Windows Management Framework (WMF). The "Alpha" version of PowerShell 6 is cross-platform (Windows, Linux, and OS X) and needs to be downloaded and installed from this release page.
Additional resources:
There are multiple ways to run a foreach-loop in PowerShell and they all bring their own advantages and disadvantages:
Solution | Advantages | Disadvantages |
---|---|---|
Foreach statement | Fastest. Works best with static collections (stored in a variable). | No pipeline input or output |
ForEach() Method | Same scriptblock syntax as Foreach-Object , but faster. Works best with static collections (stored in a variable). Supports pipeline output. | No support for pipeline input. Requires PowerShell 4.0 or greater |
Foreach-Object (cmdlet) | Supports pipeline input and output. Supports begin and end-scriptblocks for initialization and closing of connections etc. Most flexible solution. | Slowest |
$foreach = Measure-Command { foreach ($i in (1..1000000)) { $i * $i } }
$foreachmethod = Measure-Command { (1..1000000).ForEach{ $_ * $_ } }
$foreachobject = Measure-Command { (1..1000000) | ForEach-Object { $_ * $_ } }
"Foreach: $($foreach.TotalSeconds)"
"Foreach method: $($foreachmethod.TotalSeconds)"
"ForEach-Object: $($foreachobject.TotalSeconds)"
Example output:
Foreach: 1.9039875
Foreach method: 4.7559563
ForEach-Object: 10.7543821
While Foreach-Object
is the slowest, it's pipeline-support might be useful as it lets you process items as they arrive (while reading a file, receiving data etc.). This can be very useful when working with big data and low memory as you don't need to load all the data to memory before processing.
$PSCmdlet.ShouldProcess()
will also automatically write a message to the verbose output.
PS> Invoke-MyCmdlet -Verbose
VERBOSE: Performing the operation "Invoke-MyCmdlet" on target "Target of action"
This topic is documenting the switch statement used for branching the flow of the script. Do not confuse it with switch parameters which are used in functions as boolean flags.
In most cases, the input of the pipeline will be an array of objects. Although the behavior of the PROCESS{}
block may seem similar to the foreach{}
block, skipping an element in the array requires a different process.
If, like in foreach{}
, you used continue
inside the PROCESS{}
block, it would break the pipeline, skipping all following statements including the END{}
block. Instead, use return
- it will only end the PROCESS{}
block for the current element and move to the next.
In some cases, there is a need to output the result of functions with different encoding. The encoding of the output of the CmdLets is controlled by the $OutputEncoding
variable. When the output is intended to be put into a pipeline to native applications, it might be a good idea to fix the encoding to match the target $OutputEncoding = [Console]::OutputEncoding
Additional references:
Blog article with more insight about $OutputEncoding
https://blogs.msdn.microsoft.com/powershell/2006/12/11/outputencoding-to-the-rescue/
PowerShell Jobs run in a new process. This has pros and cons which are related.
Pros:
Cons:
You can read more about the return semantics on the about_Return page on TechNet, or by invoking get-help return
from a PowerShell prompt.
Notable Q&A question(s) with more examples/explanation:
about_return on MSDN explains it succinctly:
The Return keyword exits a function, script, or script block. It can be used to exit a scope at a specific point, to return a value, or to indicate that the end of the scope has been reached.
Users who are familiar with languages like C or C# might want to use the Return keyword to make the logic of leaving a scope explicit.
In Windows PowerShell, the results of each statement are returned as output, even without a statement that contains the Return keyword. Languages like C or C# return only the value or values that are specified by the Return keyword.
Strings are objects representing text.
Powershell naming system has quite strict rules of naming cmdlets (Verb-Noun template; see [topic not yet created] for more information). But it is not really convenient to write Get-ChildItems
every time you want to list files in directory interactively.
Therefore Powershell enables using shortcuts - aliases - instead of cmdlet names.
You can write ls
, dir
or gci
instead of Get-ChildItem
and get the same result. Alias is equivalent to its cmdlet.
Some of the common aliases are:
alias | cmdlet |
---|---|
%, foreach | For-EachObject |
?, where | Where-Object |
cat, gc, type | Get-Content |
cd, chdir, sl | Set-Location |
cls, clear | Clear-Host |
cp, copy, cpi | Copy-Item |
dir/ls/gci | Get-ChildItem |
echo, write | Write-Output |
fl | Format-List |
ft | Format-Table |
fw | Format-Wide |
gc, pwd | Get-Location |
gm | Get-Member |
iex | Invoke-Expression |
ii | Invoke-Item |
mv, move | Move-Item |
rm, rmdir, del, erase, rd, ri | Remove-Item |
sleep | Start-Sleep |
start, saps | Start-Process |
In the table above, you can see how aliases enabled simulating commands known from other environments (cmd, bash), hence increased discoverability.
Profile file is a powershell script that will run while the powershell console is starting. This way we can have our environment prepared for us each time we start new powershell session.
Typical things we want to do on powershell start are:
There are several profile files and locations that have different uses and also hierarchy of start-up order:
Host | User | Path | Start order | Variable |
---|---|---|---|---|
All | All | %WINDIR%\System32\WindowsPowerShell\v1.0\profile.ps1 | 1 | $profile.AllUsersAllHosts |
All | Current | %USERPROFILE%\Documents\WindowsPowerShell\profile.ps1 | 3 | $profile.CurrentUserAllHosts |
Console | All | %WINDIR%\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1 | 2 | $profile.AllUsersCurrentHost |
Console | Current | %USERPROFILE%\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1 | 4 | $profile.CurrentUserCurrentHost |
ISE | All | %WINDIR%\System32\WindowsPowerShell\v1.0\Microsoft.PowerShellISE_profile.ps1 | 2 | $profile.AllUsersCurrentHost |
ISE | Current | %USERPROFILE%\Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1 | 4 | $profile.CurrentUserCurrentHost |
#requires
statement can be placed on any line in the script (it doesn't have to be the first line) but it must be the first statement on that line.
Multiple #requires
statements may be used in one script.
For more reference, please refer to official documentation on Technet - about_about_Requires.
Get-Help
is a cmdlet for reading help topics in PowerShell.
Read more a TechNet
Note: The Array expression operator or @()
have very different behavior than the Splatting operator @
.
Read more at about_Splatting @ TechNet
Signing a script will make your scripts comply with all exeuction policies in PowerShell and ensure the integrity of a script. Signed scripts will fail to run if they have been modified after being signed.
Scripts signing requires a code signing certificate. Recommendations:
Read more at about_Signing @ TechNet
PowerShell has configurable execution policies that control which conditions are required for a script or configuration to be executed. An excecution policy can be set for multiple scopes; computer, current user and current process. Execution policies can easily be bypassed and is not designed to restrict users, but rather protect them from violating signing policies unintentionally.
The available policies are:
Setting | Description |
---|---|
Restricted | No scripts allowed |
AllSigned | All scripts need to be signed |
RemoteSigned | All local scripts allowed; only signed remote scripts |
Unrestricted | No requirements. All scripts allowed, but will warn before running scripts downloaded from the internet |
Bypass | All scripts are allowed and no warnings are displayed |
Undefined | Remove the current execution policy for the current scope. Uses the parent policy. If all policies are undefined, restricted will be used. |
You can modify the current execution policies using Set-ExecutionPolicy
-cmdlet, Group Policy or the -ExecutionPolicy
parameter when launching a powershell.exe
process.
Read more at about_Execution_Policies @ TechNet
Pester is a test framework for PowerShell that allows you to run test cases for you PowerShell code. It can be used to run ex. unit tests to help you verify that your modules, scripts etc. work as intended.
Common parameters can be used with any cmdlet (that means as soon as you mark your function as cmdlet [see CmdletBinding()
], you get all of these parameters for free).
Here is the list of all common parameters (alias is in parenthesis after corresponding parameter):
-Debug (db)
-ErrorAction (ea)
-ErrorVariable (ev)
-InformationAction (ia) # introduced in v5
-InformationVariable (iv) # introduced in v5
-OutVariable (ov)
-OutBuffer (ob)
-PipelineVariable (pv)
-Verbose (vb)
-WarningAction (wa)
-WarningVariable (wv)
-WhatIf (wi)
-Confirm (cf)
As of PowerShell 3.0, there are two ways to work with management classes in PowerShell, WMI and CIM. PowerShell 1.0 and 2.0 only supported the WMI-module which is now superseeded by the new and improved CIM-module. In a later release of PowerShell, the WMI-cmdlets will be removed.
Comparison of CIM and WMI-modules:
CIM-cmdlet | WMI-cmdlet | What it does |
---|---|---|
Get-CimInstance | Get-WmiObject | Gets CIM/WMI-objects for a class |
Invoke-CimMethod | Invoke-WmiMethod | Invokes a CIM/WMI-class method |
Register-CimIndicationEvent | Register-WmiEvent | Registers event for a CIM/WMI-class |
Remove-CimInstance | Remove-WmiObject | Remove CIM/WMI-object |
Set-CimInstance | Set-WmiInstance | Updates/Saves CIM/WMI-object |
Get-CimAssociatedInstance | N/A | Get associated instances (linked object/classes) |
Get-CimClass | Get-WmiObject -List | List CIM/WMI-classes |
New-CimInstance | N/A | Create new CIM-object |
Get-CimSession | N/A | Lists CIM-sessions |
New-CimSession | N/A | Create new CIM-session |
New-CimSessionOption | N/A | Creates object with session options; protocol, encoding, disable encryption etc. (for use with New-CimSession ) |
Remove-CimSession | N/A | Removes/Stops CIM-session |
Should I use CIM or WMI with Windows PowerShell? @ Hey, Scripting Guy! Blog
See also Comparison Operators, which can be used in conditional expressions.
The regular expression used in the Decode URL examples was taken from RFC 2396, Appendix B: Parsing a URI Reference with a Regular Expression; for posterity, here's a quote:
The following line is the regular expression for breaking-down a URI reference into its components.
^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? 12 3 4 5 6 7 8 9
The numbers in the second line above are only to assist readability; they indicate the reference points for each subexpression (i.e., each paired parenthesis). We refer to the value matched for subexpression as $. For example, matching the above expression to
http://www.ics.uci.edu/pub/ietf/uri/#Related
results in the following subexpression matches:
$1 = http: $2 = http $3 = //www.ics.uci.edu $4 = www.ics.uci.edu $5 = /pub/ietf/uri/ $6 = <undefined> $7 = <undefined> $8 = #Related $9 = Related
The most hard part is to attach a subdocument into the document which hasn't created yet if we need the subdocument need to be in the expected looking we will need to iterate with a for loop the array into a variable and using $doc2.add("Key", "Value")
instead using the foreach
current array with index. This will make the subdocument in two lines as you can see in the "Tags" = [MongoDB.Bson.BsonDocument] $doc2
.
An important concept which relies on Hash Tables is Splatting. It is very useful for making a large number of calls with repetitive parameters.
Please remember that PowerShell's Help System is one of the best resources you can possibly utilize.
Get-Help Get-ADUser -Full
Get-Help Get-ADGroup -Full
Get-Help Get-ADComputer -Full
Get-Help Get-ADObject -Full
All of the help documentation will provide examples, syntax and parameter help.
You can use the below function if in case you are not able to import SQLPS module
function Import-Xls
{
[CmdletBinding(SupportsShouldProcess=$true)]
Param(
[parameter(
mandatory=$true,
position=1,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)]
[String[]]
$Path,
[parameter(mandatory=$false)]
$Worksheet = 1,
[parameter(mandatory=$false)]
[switch]
$Force
)
Begin
{
function GetTempFileName($extension)
{
$temp = [io.path]::GetTempFileName();
$params = @{
Path = $temp;
Destination = $temp + $extension;
Confirm = $false;
Verbose = $VerbosePreference;
}
Move-Item @params;
$temp += $extension;
return $temp;
}
# since an extension like .xls can have multiple formats, this
# will need to be changed
#
$xlFileFormats = @{
# single worksheet formats
'.csv' = 6; # 6, 22, 23, 24
'.dbf' = 11; # 7, 8, 11
'.dif' = 9; #
'.prn' = 36; #
'.slk' = 2; # 2, 10
'.wk1' = 31; # 5, 30, 31
'.wk3' = 32; # 15, 32
'.wk4' = 38; #
'.wks' = 4; #
'.xlw' = 35; #
# multiple worksheet formats
'.xls' = -4143; # -4143, 1, 16, 18, 29, 33, 39, 43
'.xlsb' = 50; #
'.xlsm' = 52; #
'.xlsx' = 51; #
'.xml' = 46; #
'.ods' = 60; #
}
$xl = New-Object -ComObject Excel.Application;
$xl.DisplayAlerts = $false;
$xl.Visible = $false;
}
Process
{
$Path | ForEach-Object {
if ($Force -or $psCmdlet.ShouldProcess($_)) {
$fileExist = Test-Path $_
if (-not $fileExist) {
Write-Error "Error: $_ does not exist" -Category ResourceUnavailable;
} else {
# create temporary .csv file from excel file and import .csv
#
$_ = (Resolve-Path $_).toString();
$wb = $xl.Workbooks.Add($_);
if ($?) {
$csvTemp = GetTempFileName(".csv");
$ws = $wb.Worksheets.Item($Worksheet);
$ws.SaveAs($csvTemp, $xlFileFormats[".csv"]);
$wb.Close($false);
Remove-Variable -Name ('ws', 'wb') -Confirm:$false;
Import-Csv $csvTemp;
Remove-Item $csvTemp -Confirm:$false -Verbose:$VerbosePreference;
}
}
}
}
}
End
{
$xl.Quit();
Remove-Variable -name xl -Confirm:$false;
[gc]::Collect();
}
}
Not sure if this is the best way to handle documenting Automatic Variables, yet this is better than nothing. Please comment if you find a better way :)
A class-based DSC Resource must:
[DscResource()]
attributeTest()
method that returns [bool]
Get()
method that returns its own object type (eg. [Ticket]
)Set()
method that returns [void]
Key
DSC PropertyAfter creating a class-based PowerShell DSC Resource, it must be "exported" from a module, using a module manifest (.psd1) file. Within the module manifest, the DscResourcesToExport
hashtable key is used to declare an array of DSC Resources (class names) to "export" from the module. This enables consumers of the DSC module to "see" the class-based resources inside the module.
The PowerShell Workflow feature is exclusively supported on the Microsoft Windows platform, under PowerShell Desktop Edition. PowerShell Core Edition, which is supported on Linux, Mac, and Windows, does not support the PowerShell Workflow feature.
When authoring a PowerShell Workflow, keep in mind that workflows call activities, not cmdlets. You can still call cmdlets from a PowerShell Workflow, but the Workflow Engine will implicitly wrap the cmdlet invocation in an InlineScript
activity. You can also explicitly wrap code inside of the InlineScript
activity, which executes PowerShell code; by default the InlineScript
activity runs in a separate process, and returns the result to the calling Workflow.
In later versions of PowerShell, Remove-TypeData has been added to the PowerShell cmdlet libraries which can allow for removal of a type within a session. For more details on this cmdlet, go here: https://msdn.microsoft.com/en-us/powershell/reference/4.0/microsoft.powershell.utility/remove-typedata
For those experience with .NET it goes without saying that the differing versions of C# can be quite radically different in their level of support for certain syntax.
If utilising Powershell 1.0 and/or -Language CSharp, the managed code will be utilising .NET 2.0 which is lacking in a number of features which C# developers typically use without a second thought these days, such as Generics, Linq and Lambda. On top of this is formal polymorphism, which is handled with defaulted parameters in later versions of C#/.NET.
See MSDN Microsoft.PowerShell.Archive (5.1) for further reference