To get started with this blank [[TiddlyWiki]], you'll need to modify the following tiddlers:
* [[SiteTitle]] & [[SiteSubtitle]]: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* [[MainMenu]]: The menu (usually on the left)
* [[DefaultTiddlers]]: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>

/***
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which need larger font sizes.
***/
/*{{{*/
body {font-size:0.8em;}
#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}
.subtitle {font-size:0.8em;}
.viewer table.listView {font-size:0.95em;}
/*}}}*/

Many mobile phone service providers offer public hotspots at airports and public places. To connect, you typically need to browse to a logon page, and then manually enter your credentials.
Here is a script that does this automatically. It is tailored to t-mobile.de but in the script, you can see the places that need adjustment for other providers:
<html><pre>function Start-Hotspot
{
param
(
[System.String]
$Username = 'XYZ@t-mobile.de',
[System.String]
$Password = 'topsecret'
)
# change this to match your provider logon page URL
$url = 'https://hotspot.t-mobile.net/wlan/start.do'
$r = Invoke-WebRequest -Uri $url -SessionVariable fb
$form = $r.Forms[0]
# change this to match the website form field names:
$form.Fields['username'] = $Username
$form.Fields['password'] = $Password
# change this to match the form target URL
$r = Invoke-WebRequest -Uri ('https://hotspot.t-mobile.net' + $form.Action) -WebSession $fb -Method POST -Body $form.Fields
Write-Host 'Connected' -ForegroundColor Green
Start-Process 'http://www.google.de'
}</pre></html>
In a nutshell, Invoke-WebRequest can navigate to a page, fill out form data, and then send the form back. To do this right, you will want to look at the source code of the logon web page (browse to the page, then right-click the page in your browser and display the source HTML code).
Next, identify the form that you want to fill out, and change the form field names and action according to the form that you identified in the HTML code.

Sometimes you may want to convert an IP address to its decimal value, for example, because you want to use binary operators to set bits. Here are two simple filters that make this a snap:
<html><pre>filter Convert-IP2Decimal
{
([IPAddress][String]([IPAddress]$_)).Address
}</pre></html>
<html><pre>filter Convert-Decimal2IP
{
([System.Net.IPAddress]$_).IPAddressToString
}</pre></html>

Converting Binary Data to IP Address (and vice versa)
In a previous tip we showed how you can display an IPv4 address as a binary. Here's an even faster (and more cryptic) way:
$ipV4 = '192.168.12.33'
[Convert]::toString(([IPAddress][String]([IPAddress]$ipV4).Address).Address,2)
The result looks like this:
11000000101010000000110000100001
Now how would you do the opposite and turn a binary into an IP address? Here's how:
$IPBinary = '11000000101010000000110000100001'
([System.Net.IPAddress]"$([System.Convert]::ToInt64($IPBinary,2))"
).IPAddressToString

When you use Where-Object to filter information by date or time, this works actually very well -provided you use the correct filtering format. Do not use the format found in the results.
To specify a date and or time, alwa</pre></html> ys use the culture-neutral management format:
"year-month-day hour:minute:second", so May 14, 2014 at 12:30 would read like this: "2014-05-12 12:30:00".
Or to put it differently: when you output results, PowerShell formats dates and times according to your control panel settings. When you input information (for example, filter criteria), PowerShell always expects a generic date and time format. Which makes sense: scripts should run in any culture the same. Results should be formatted in the culture of the person that needs to read them.
So to find all files in your Windows folder that have not changed since April 30, 2012, try this:
<html><pre>PS> Get-ChildItem -Path c:\Windows -File | Where-Object LastWriteTime -lt '2012-04-30'
Directory: C:\windows
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 21.11.2010 04:24 71168 bfsvc.exe
-a--- 29.01.2012 14:19 9106 ColorPicker for PowerPoint Setup
Log.txt
-a--- 09.01.2012 23:15 133012 DPINST.LOG
-a--- 08.01.2012 10:34 2790 DtcInstall.log
...
-a--- 10.06.2009 23:41 94784 twain.dll
-a--- 21.11.2010 04:25 51200 twain_32.dll
-a--- 10.06.2009 23:41 49680 twunk_16.exe
-a--- 14.07.2009 03:14 31232 twunk_32.exe
-a--- 10.06.2009 22:31 51867 Ultimate.xml
-a--- 14.07.2009 03:14 9728 winhlp32.exe
-a--- 10.06.2009 22:52 316640 WMSysPr9.prx
-a--- 14.07.2009 03:39 10240 write.exe
PS></pre></html>

Ever needed a new local administrator account for testing purposes? Provided you are already Administrator, and you opened a PowerShell with full Administrator privileges, adding such a user is a matter of just a couple of lines of code:
<html><pre>$user = 'splitpersonality'
net user /add $user
net localgroup Administrators /add $user</pre></html>
Note that the target group name is localized, so on non-US systems, you need to replace "Administrators" with the localized name of your Administrators group.

Sort-Object has an awesome feature: with the parameter -Unique, you can remove duplicates:
<html><pre>PS> 1,3,2,3,2,1,2,3,4,7 | Sort-Object
1
1
2
2
2
3
3
3
4
7</pre></html>
<html><pre>PS> 1,3,2,3,2,1,2,3,4,7 | Sort-Object -Unique
1
2
3
4
7
PS> </pre></html>
This can be applied to object results, as well. Check out this example: it will get you the latest 40 errors from your system event log:
Get-EventLog -LogName System -EntryType Error -Newest 40 | Sort-Object -Property InstanceID, Message | Out-GridView
This may be perfectly fine, but depending on your event log, you may also get duplicate entries.
With -Unique, you can eliminate duplicates, based on multiple properties:
Get-EventLog -LogName System -EntryType Error -Newest 40 | Sort-Object -Property InstanceID, Message -Unique | Out-GridView
You will no longer see more than one entry with the same InstanceID AND Message.
You can then sort the result again, to get back the chronological order:
Get-EventLog -LogName System -EntryType Error -Newest 40 | Sort-Object -Property InstanceID, Message -Unique | Sort-Object -Property TimeWritten -Descending | Out-GridView
So bottom line is: Sort-Objects parameter -Unique can be applied to multiple properties at once.

You can easily convert object results to CSV files in PowerShell. This generates a CSV report of current processes:
<html><pre>PS> Get-Process | Export-Csv $env:temp\report.csv -UseCulture -Encoding UTF8 -NoTypeInformation</pre></html>
To open the CSV file in Microsoft Excel, you could use Invoke-Item to open the file, but this would only work if the file extension CSV is actually associated with the Excel application.
This script will open the CSV file always in Microsoft Excel. It illustrates a way to find out the actual path to your Excel application (it assumes it is installed and does not check if it is missing altogether though):
<html><pre>$report = "$env:temp\report.csv"
$ExcelPath = 'C:\Program Files*\Microsoft Office\OFFICE*\EXCEL.EXE'
$RealExcelPath = Resolve-Path -Path $ExcelPath | Select-Object -First 1 -ExpandProperty Path
& $RealExcelPath $report</pre></html>

Comparison operators act like filters when applied to arrays. So any console command that outputs multiple text lines can be used with comparison operators.
This example will use netstat.exe to get only established network connections, then to get only established network connections to any server that has "stor" in its name, and then uses ipconfig to get the current IPv4 address:
<html><pre>PS> @(netstat) -like '*establ*'
TCP 127.0.0.1:1028 TobiasAir1:5354 ESTABLISHED
TCP 127.0.0.1:1034 TobiasAir1:27015 ESTABLISHED
TCP 127.0.0.1:1072 TobiasAir1:19872 ESTABLISHED
TCP 127.0.0.1:5354 TobiasAir1:1028 ESTABLISHED
TCP 127.0.0.1:19872 TobiasAir1:1072 ESTABLISHED
TCP 127.0.0.1:27015 TobiasAir1:activesync ESTABLISHED
TCP 192.168.2.106:2208 STORAGE1:1138 ESTABLISHED
TCP 192.168.2.106:2298 snt-re3-7a:http ESTABLISHED
PS> @(netstat) -like '*stor*establ*'
TCP 192.168.2.106:2208 STORAGE1:1138 ESTABLISHED
PS> @(ipconfig) -like '*IPv4*'
IPv4 Address. . . . . . . . . . . : 192.168.2.106</pre></html>
The trick is to enclose the console command in @() which makes sure that the result is always an array.

Often, you might want to browse all system events around a given date. Let's say a machine crashed at 08:47, and you'd like to see all events +/− 2 minutes around that time.
Here is a script that does It for you:
<html><pre>$deltaminutes = 2
$delta = New-TimeSpan -Minutes $deltaminutes
$time = Read-Host -Prompt 'Enter time of event (yyyy-mm-hh HH:MM:SS or HH:MM)'
$datetime = Get-Date -Date $time
$start = $datetime - $delta
$end = $datetime + $delta
$result = @(Get-EventLog -LogName System -Before $end -After $start)
$result += Get-EventLog -LogName Application -Before $end -After $start
$result | Sort-Object -Property TimeGenerated -Descending |
Out-GridView -Title "Events +/− $deltaminutes minutes around $datetime" </pre></html>
When you run it, it asks for a time or a date and time. Next, you get back all events that occurred within 2 minutes before and after in the system and application log.

If you run a large Active Directory, you should use specific Active Directory cmdlets or management functions. However, if you just want to know the groups a given user account belongs to, and if the user account can also be a non-domain local account, then WMI may yield the information you need. Here's a little function to play with:
<html><pre>function Get-GroupMembership
{
param(
$UserName = $env:username,
$Domain = $env:userdomain
)
$user = Get-WmiObject -Class Win32_UserAccount -Filter "Name='$UserName' and Domain='$Domain'"
$user.GetRelated('Win32_Group')
}</pre></html>
By default, it returns the group memberships of the account that runs the script but you can use the parameters -UserName and -Domain to also specify a different account.
If you want to access a local account on a different machine, then add the parameter -ComputerName to Get-WmiObject. If you want to use PowerShell remoting rather than DCOM to remotely connect to another machine, you may want to use the new CIM cmdlets instead (Get-CimInstance instead of Get-WmiObject).

Often, users need to format numbers and limit the number of digits, or add leading zeros. There is one simple and uniform strategy for this: the operator "-f"!
Let's make sure a number has leading zeros:
<html><pre>$number = 68
'{0:d7}' -f $number</pre></html>
This will produce a 7-digit number with leading zeros. Adjust the number after "d" to control the number of digits.
To limit the number of digits, use "n" instead of "d". This time, the number after "n" controls the number of digits:
<html><pre>$number = 35553568.67826738
'{0:n1}' -f $number</pre></html>
Likewise, use "p" to format percentages:
<html><pre>$number = 0.32562176536
'{0:p2}' -f $number</pre></html>

If you need a simple way of creating random passwords, then this piece of code may help you:
<html><pre>$Assembly = Add-Type -AssemblyName System.Web
[System.Web.Security.Membership]::GeneratePassword(10,4)</pre></html>
GeneratePassword() takes two arguments. The first sets the length of the password. The second sets the number of non-alphanumeric characters that you want in it.

Gets basic information about cmdlets and other elements of Windows Power...

Gets the content of the item at the specified location.

Gets performance counter data from local and remote computers.

Gets the Credential Security Service Provider-related configuration for ...

Displays management information for a resource instance specified by a R...

Gets events from event logs and event tracing log files on local and rem...

There is a tiny .NET function called GetHostByName() that is vastly useful. It will look up a host name and return its current IP address:
<html><pre>[System.Net.DNS]::GetHostByName('someName')</pre></html>
With just a simple PowerShell wrapper, this is turned into a great little function that is extremely versatile:
<html><pre>function Get-IPAddress
{
param
(
[Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[String[]]
$Name
)
process
{ $Name | ForEach-Object { try { [System.Net.DNS]::GetHostByName($_) } catch { } }}</pre></html>
}
You can now run the function as-is (to get your own IP address). You can submit one or more computer names (comma separated). You can even pipe in data from Get-ADComputer or Get-QADComputer.
<html><pre>Get-IPAddress
Get-IPAddress -Name TobiasAir1
Get-IPAddress -Name TobiasAir1, Server12, Storage1
'TobiasAir1', 'Server12', 'Storage1' | Get-IPAddress
Get-QADComputer | Get-IPAddress
Get-ADComputer -Filter * | Get-IPAddress</pre></html>
This is possible because the function has both a pipeline binding and an argument serializer.
The -Name argument is fed to ForEach-Object, so no matter how many computer names a user specifies, they all get processed.
The -Name parameter accepts value from the pipeline both by property and as a value. So you can feed in any object that has a "Name" property, but you can also feed in any list of plain strings.
Note that the function has a very simple error handler. If you submit a computer name that cannot be resolved, nothing happens. Add code to the catch block if you want an error message instead.

To get started with this blank [[TiddlyWiki]], you'll need to modify the following tiddlers:
* [[SiteTitle]] & [[SiteSubtitle]]: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* [[MainMenu]]: The menu (usually on the left)
* [[DefaultTiddlers]]: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>

To get information about the properties and methods of an object retrieve an instance of that object and then pipe the object to the Get-Member cmdlet. For example, this command returns the properties and methods available when working with processes:
<html><pre>Get-Process | Get-Member</pre></html>

To access command-line arguments used when starting a script use the automatic variable $args. You can cycle through the individual arguments in the $args collection by using code similar to this:
<html><pre>foreach ($i in $args) {$i} </pre></html>
To access a particular argument use the collection index number, with 0 representing the first item in the collection, 1 representing the second item, etc:
<html><pre>$args[0]</pre></html>
You can reference the last item in a collection by using the index number 1:
<html><pre>$args[-1]</pre></html>

To bind to an Active Directory account use the LDAP provider:
<html><pre>$a = [adsi] "LDAP://cn=kenmyer, `
ou=Finance, dc=fabrikam, dc=com"</pre></html>
Listing all the objects in an OU is a little more complicated; however, one relatively easy way to accomplish this task is to bind to the OU and then use the PSBase_GetChildren() method to retrieve a collection of items stored in that OU:
<html><pre>$objOU = [ADSI]`
"LDAP://ou=Finance,dc=fabrikam,dc=com"
$users = $objOU.PSBase.Get_Children()
$users | Select-Object displayName</pre></html>

To bind to a local account, use the WinNT provider:
<html><pre>$a = [adsi] "WinNT://atl-ws-01/kenmyer"
$a.FullName</pre></html>

To run scripts from within Windows PowerShell you will need to change your security settings; by default, PowerShell only runs scripts signed by a trusted authority. To enable PowerShell to run all locally-created scripts (regardless of whether or not they have been signed) use the following command:
<html><pre>Set-ExecutionPolicy RemoteSigned</pre></html>

To enable simple copying and pasting in the Windows PowerShell console do the following:
Start Windows PowerShell, then click the icon in the upper left-hand corner and choose Properties.
In the Windows PowerShell Properties dialog box, on the Options tab, select QuickEdit Mode and then click OK.
To copy text in the console window select the text and then press ENTER. To paste text into the window click the right mouse button.

To put multiple commands on a single line, separate those commands using a semicolon:
<html><pre>$a = 1,2,3,4,5; $b = $a[2]; Write-Host $b</pre></html>

To work with a COM object use the New-Object cmdlet followed by the comobject parameter and the appropriate ProgID:
<html><pre>$a = New-Object -comobject `
"Excel.Application"
$a.Visible = $True</pre></html>

To get complete help information for a Windows PowerShell cmdlet, use the Get-Help cmdlet along with the full parameter. For example, to view the help information for the Get-Process cmdlet type the following:
<html><pre>Get-Help Get-Process -full</pre></html>
To view the example commands for a cmdlet use the
examples parameter:
<html><pre>Get-Help Get-Process -examples</pre></html>
If you cant remember the exact name for a cmdlet use Get-Command to retrieve a list of all the cmdlets available to you:
<html><pre>Get-Command</pre></html>
For a list of available aliases, use the Get-Alias cmdlet:
<html><pre>Get-Alias</pre></html>

To insert a comment, use the pound sign (#):
<html><pre># This is a comment, not a line to be run.</pre></html>

To insert a line break into a Windows PowerShell script use the backtick (`) :
<html><pre>Write-Host `
"This is a continuation of the line."</pre></html>
You can also break a line at the pipe separator (|) character (assuming your line uses the pipeline):
<html><pre>Get-ChildItem C:\Scripts |
Sort-Object Length Descending</pre></html>

To insert a paragraph return in your output use the newline character `n:
<html><pre>Write-Host "Line 1.`nLine 2."</pre></html>

Windows PowerShell cmdlets (like Where-Object) use a special set of comparison operators, including those shown in the following table.
Each of these operators can be made case sensitive by adding a c immediately after the hyphen. For example, -ceq represents the case-sensitive equals operator; -clt is the case-sensitive less than operator.
|!Operator|!Description|
|-lt|Less than|
|-le|Less than or equal to|
|-gt|Greater than|
|-ge|Greater than or equal to|
|-eq|Equal to|
|-ne|Not equal to|
|-like|Like (uses wildcards for matching)|
|-notlike|Not like (uses wildcards for matching)|

To print data to the default printer use the Out-Printer cmdlet:
<html><pre>Get-Process | Out-Printer</pre></html>

To read the contents of a text file into a variable, call the Get-Content cmdlet followed by the path to the text file:
<html><pre>$a = Get-Content C:\Scripts\Test.txt</pre></html>
Each line in the file ends up as an item in the array $a. If you want to access a single line in the file you can simply specify the index number corresponding to that line:
<html><pre>$a[0]</pre></html>
This command echoes back the last line in $a:
<html><pre>$a[-1]</pre></html>
Bonus. To determine the number of lines, words, and characters in a text file use this command:
<html><pre>get-content c:\scripts\test.txt |
measure-object -line -word -character</pre></html>

To run a script from within Windows PowerShell, type the full path to the script (or type the script name if the script is stored in a folder that is part of your Windows path):
<html><pre>C:\Scripts\Test.ps1</pre></html>
If the path name includes blank spaces you must preface the path with an ampersand and enclose the path in double quotes. For example:
<html><pre>&"C:\Scripts\My Scripts\test.ps1"</pre></html>
From outside Windows PowerShell (e.g., from the Run dialog box or from a Cmd.exe window) you must call Windows PowerShell and then pass the script path as an argument to that call:
<html><pre>powershell.exe noexit C:\Scripts\Test.ps1</pre></html>
The -noexit parameter ensures that the PowerShell window remains open after the script finishes running.

To work with or display specified properties of a collection, pipe the returned results to the Select-Object cmdlet:
<html><pre>Get-Process | Select-Object Name, Company</pre></html>

To sort data returned by Windows PowerShell simply pipe that data to the Sort-Object cmdlet, specifying the property you want to sort by:
<html><pre>Get-Process | Sort-Object ID</pre></html>
You can also add the descending or -ascending parameters to specify a sort order:
<html><pre>Get-Process | Sort-Object ID descending</pre></html>
You can even sort by multiple properties:
<html><pre>Get-Process | Sort-Object ProcessName, ID</pre></html>

To display text in a different color use the Write-Host cmdlet and specify a foreground color:
<html><pre>Write-Host "test" -foregroundcolor "green"</pre></html>
You can also specify a different background color:
<html><pre>Write-Host "test" -backgroundcolor "red"</pre></html>

To get computer information using WMI call the Get-WMIObject cmdlet followed by the class name:
<html><pre>Get-WMIObject Win32_BIOS</pre></html>
If the class you are interested in does not reside in the cimv2 namespace simply include the namespace parameter:
<html><pre>Get-WMIObject SystemRestore `
-namespace root\default</pre></html>
To access data on another computer use the
computername parameter:
<html><pre>Get-WMIObject Win32_BIOS `
computername atl-ws-01</pre></html>
To limit returned data, use a WQL query and the query parameter:
<html><pre>Get-WMIObject -query `
"Select * From Win32_Service `
Where State = 'Stopped'"</pre></html>

To write an If statement use code similar to this:
<html><pre>$a = "white"
if ($a -eq "red")
{"The color is red."}
elseif ($a -eq "white")
{"The color is white."}
else
{"The color is blue."} </pre></html>
Instead of writing a series of If statements you can use a Switch statement, which is equivalent to VBScripts Select Case statement:
<html><pre>$a = 2
switch ($a)
{
1 {"The color is red."}
2 {"The color is blue."}
3 {"The color is green."}
4 {"The color is yellow."}
default {"Other."}
}</pre></html>

To write a Do loop use code like the following, replacing the code between the curly braces with the code to be executed on each iteration of the loop. Oh: and replacing the code inside the parentheses with the loop condition:
<html><pre>$a = 1
do {$a; $a++}
while ($a -lt 10)
$a = 1
do {$a; $a++}
until ($a gt 10)
</pre></html>

To write a For statement use code similar to this:
<html><pre>for ($a = 1; $a -le 10; $a++) {$a}</pre></html>
By comparison, a For Each statement might look like this:
<html><pre>foreach ($i in get-childitem c:\scripts)
{$i.extension} </pre></html>

To echo a message in reverse video use the Write-Warning cmdlet:
<html><pre>Write-Warning "An error has occurred."</pre></html>

To save data to a text file use the Out-File cmdlet:
<html><pre>Get-Process | Out-File C:\Scripts\Test.txt</pre></html>
To append data to an existing file, add the append parameter:
<html><pre>Get-Process | Out-File C:\Test.txt append</pre></html>
You can also use the MS-DOS redirection characters (> for write, >> for append) when using Windows PowerShell. This command writes data to the file C:\Scripts\Test.txt:
<html><pre>Get-Process > C:\Scripts\Test.txt</pre></html>
Another option is to use the Export-CSV cmdlet to save data as a comma-separated-values file:
<html><pre>Get-Process | Export-CSV C:\Test.csv</pre></html>

When you launch an EXE file, PowerShell will happily start it, then continue and not care about it anymore:
<html><pre>PS> notepad
PS></pre></html>
If you'd like to keep a handle to the process, for example to find out its process ID, or to be able to check back later how the process performs, or to kill it, use Start-Process and the –PassThru parameter. This returns a process object:
<html><pre>PS> $process = Start-Process -FilePath notepad -PassThru
PS> $process.Id
1840
PS> 'You just launched process {0}.' -f $process.Id
You just launched process 1840.
PS> 'CPU consumption in seconds so far: {0}.' -f $process.CPU
CPU consumption in seconds so far: 0,0468003.
PS> $process.CloseMainWindow()
True
PS> </pre></html>

Microsoft Excel is an example of a program that is not easy to launch directly: the path to Excel may be different, depending on Office version and platform (32-bit or 64-bit).
PowerShell has a very clever cmdlet to run programs: Get-Process. Traditionally, you'd use it like this to run Excel (or any other executable):
<html><pre>PS> Start-Process -FilePath 'C:\Program Files(x86)\Microsoft Office\Office14\EXCEL.EXE'</pre></html>
On your system, the path to Excel may be very different, though. Which is why Start-Process happily accepts wildcards. Just replace any "specific" part of your path with a wildcard, and you are all set.
This line will launch any Excel version, regardless of a plaLaunching Any Excel Version
tform:
<html><pre>PS> Start-Process -FilePath 'C:\Program*\MicrosoftOffice\Office*\EXCEL.EXE'</pre></html>

<html><pre>//# Uses Quest Active Roles
//# Free to download http://www.quest.com/powershell/activeroles-server.aspx
//#
//# Special Thanks to Mladen Milunovic for his comments that improved the Script!
//#
//# List all computers that have been
//# Inactive in "Active Directory"(Boy THERE'S a play on words!)
//# for a specified Number of Days
//#
//# New version exports the Data to a CSV file and removes limit
//# on number of Computers listed (default is 1000)
//#
$COMPAREDATE=GET-DATE
//#
//# DO NOT RUN THIS IN PRODUCTION. TEST IT FIRST and use it as
//# a REFERENCE tool. AUTO PURGING OF COMPUTER ACCOUNTS is
//# DANGEROUS and SILLY.
//#
//# But this little query might help you weed out accounts
//# of potentially dead systems in Active Directory
//#
//#
//# Number of Days to see if account has been active
//# Within
//#
$NumberDays=90
//#
$CSVFileLocation='C:\TEMP\OldComps.CSV'
//#
//#
GET-QADCOMPUTER -SizeLimit 0 -IncludedProperties LastLogonTimeStamp | where { ($CompareDate-$_.LastLogonTimeStamp).Days -gt $NumberDays } | Select-Object Name, LastLogonTimeStamp, OSName, ParentContainerDN | Sort-Object ModificationDate, Name | Export-CSV $CSVFileLocation
//#
//# </pre></html>

If you must make sure that a given string has a uniform width, then you can use .NET methods to pad the string appropriately:
<html><pre>$mytext = 'Test'
$paddedText = $mytext.PadLeft(15)
"Here is the text: '$paddedText'"
$paddedText = $mytext.PadRight(15)
"Here is the text: '$paddedText'"</pre></html>
This is the result:
<html><pre>Here is the text: ' Test'
Here is the text: 'Test '</pre></html>
You can even add a padding character yourself (if you do not want to pad with spaces):
<html><pre>PS> 'Hello'.PadLeft(20, '.')
...............Hello
PS> 'Hello'.PadRight(20, '_')
Hello_______________</pre></html>

Whenever a PowerShell script asks for credentials, PowerShell pops up a dialog box. You can view this by running this command:
Get-Credential
PowerShell is a console-based scripting language, and so it may be unwanted to open additional dialogs. That's why you can change the basic behavior and ask PowerShell to accept credential information right inside the console. This is a per-machine setting, so you need local Administrator privileges and must run these two lines from an elevated PowerShell console:
<html><pre>$key = "HKLM:\SOFTWARE\Microsoft\PowerShell\1\ShellIds"
Set-ItemProperty -Path $key -Name ConsolePrompting -Value $true</pre></html>

In a previous tip we explained how you can convert a string array into one big string. That's a prerequisite for quickly searching and replacing instances of words in a text file.
This example takes windowsupdate.log and replaces all instances of "error" with "ALERT", saves the changes to a new file and opens it in your text editor:
<html><pre>$oldfile = "$env:windir\WindowsUpdate.log"
$newfile = "$env:temp\newfile.txt"
$text = (Get-Content -Path $oldfile -ReadCount 0) -join "`n"
$text -replace 'error', 'ALERT' | Set-Content -Path $newfile
Invoke-Item -Path $newfile</pre></html>
In PowerShell 3.0, you can read the file content into a single string even faster by using the new switch parameter -Raw:
<html><pre>$text = Get-Content -Path $newfile -Raw</pre></html>

When a file is stored on a drive with NTFS file system, you can attach data streams to it to store hidden information.
Here is a sample that hides PowerShell code in an NTFS stream of a script. When you run this code, it creates a new PowerShell script file on your desktop, and opens the file in the ISE editor:
<html><pre>$path = "$home\Desktop\secret.ps1"
$secretCode = {
Write-Host -ForegroundColor Red 'This is a miracle!';
[System.Console]::Beep(4000,1000)
}
Set-Content -Path $path -Value '(Invoke-Expression ''[ScriptBlock]::Create((Get-Content ($MyInvocation.MyCommand.Definition) -Stream SecretStream))'').Invoke()'
Set-Content -Path $path -Stream SecretStream -Value $secretCode
ise $path</pre></html>
The new file will expose code like this
<html><pre>(Invoke-Expression '[ScriptBlock]::Create((Get-Content ($MyInvocation.MyCommand.Definition) -Stream SecretStream))').Invoke()</pre></html>
When you run the script file, it will output a red text and beeps for a second. So the newly created script actually executes the code embedded into the secret NTFS stream "SecretStream."
To attach hidden information to (any) file stored on an NTFS volume, use Add-Content or Set-Content with the -Stream parameter.
To read hidden information from a stream, use Get-Content and again specify the -Stream parameter with the name of the stream used to store the data.

To execute code in 32-bit from within a 64-bit environment (or vice versa), you can create appropriate aliases:
In a 32-bit PowerShell, you create:
<html><pre>Set-Alias Start-PowerShell64 "$env:windir\sysnative\WindowsPowerShell\v1.0\powershell.exe"</pre></html>
And in a 64-bit PowerShell, you would create:
<html><pre>Set-Alias Start-PowerShell32 "$env:windir\syswow64\WindowsPowerShell\v1.0\powershell.exe"</pre></html>
Now, it is simple to run code (and receive results).
The next example runs in a 64-bit PowerShell (as a proof, pointer sizes are 8). When you run the code in a 32-bit PowerShell, you get a pointer size of 4 which again proves that your code indeed is running in a 32-bit environment:
<html><pre>PS> [IntPtr]::Size
8
PS> Start-PowerShell32 { [IntPtr]::Size }
4</pre></html>
Note that the alias will return rich (serialized) objects that you can process as usual:
<html><pre>PS> Start-PowerShell32 { Get-Service } | Select-Object -Property DisplayName, Status</pre></html>
This works because when you submit your code to powershell.exe as a script block rather than plain text, powershell.exe returns serialized objects instead of plain text.

You probably know that Set-AuthenticodeSignature can be used to digitally sign PowerShell scripts. But did you know that this cmdlet can sign anything that supports signing? You can use it to digitally sign VBScripts (or EXE and DLL files) as well!
This piece of code would load a digital certificate from a PFX file, then scan your home folders for VBScript files, and apply a digital signature to the scripts:
<html><pre># change path to point to your PFX file:
$pfxpath = 'C:\Users\Tobias\Documents\PowerShell\testcert.pfx'
# change password to the password needed to read the PFX file:
# (this password was set when you exported the certificate to a PFX file)
$password = 'topsecret'
# load certificate
Add-Type -AssemblyName System.Security
$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.Import($pfxpath, $password, 'Exportable')
# apply signature to all VBScript files
# REMOVE -WHATIF TO ACTUALLY SIGN
Get-ChildItem -Path $home -Filter *.vbs -Recurse -ErrorAction SilentlyContinue |
Set-AuthenticodeSignature -Certificate $cert -WhatIf</pre></html>

There may be the need to add additional information to command results. Maybe you get data from different machines and want to keep a reference where the data came from. Or, you want to add a date so you know when the data was created.
Tagging objects (adding new columns with additional information) is easy. This will add a new property "SourceComputer" and a date to a service list:
<html><pre>Get-Service |
Add-Member -MemberType NoteProperty -Name SourceComputer -Value $env:COMPUTERNAME -PassThru |
Add-Member -MemberType NoteProperty -Name Date -Value (Get-Date) -PassThru |
Select-Object -Property Name, Status, SourceComputer, Date</pre></html>
Just remember that your added properties may not show up in the result until you use Select-Object and explicitly ask to show them.

Any file you download from the Internet or receive via email get marked by Windows as potentially unsafe. If the file contains executables or binaries, they will not run until you unblock the file.
PowerShell 3.0 and better can identify files with a "download mark":
<html><pre>Get-ChildItem -Path $Home\Downloads -Recurse |
Get-Item -Stream Zone.Identifier -ErrorAction Ignore |
Select-Object -ExpandProperty FileName |
Get-Item</pre></html>
You may not receive any files (if there are none with a download mark), or you may get tons of files (which can be an indication that you unpacked a downloaded ZIP file and forgot to unblock it before).
To remove the blocking, use the Unblock-File cmdlet. This would unblock all files in your download folder that are currently blocked (not touching any other files):
<html><pre>Get-ChildItem -Path $Home\Downloads -Recurse |
Get-Item -Stream Zone.Identifier -ErrorAction Ignore |
Select-Object -ExpandProperty FileName |
Get-Item |
Unblock-File</pre></html>

Beginning in PowerShell 3.0, there is a new automatic variable available called $PSScriptRoot. This variable previously was only available within modules. It always points to the folder the current script is located in (so it only starts to be useful once you actually save a script before you run it).
You can use $PSScriptRoot to load additional resources relative to your script location. For example, if you decide to place some functions in a separate "library" script that is located in the same folder, this would load the library script and import all of its functions:
<html><pre># this loads the script "library1.ps1" if it is located in the very
# same folder as this script.
# Requires PowerShell 3.0 or better.
. "$PSScriptRoot\library1.ps1" </pre></html>
Likewise, if you would rather want to store your library scripts in a subfolder, try this (assuming the library scripts have been placed in a folder called "resources" that resides in the same folder as your script:
<html><pre># this loads the script "library1.ps1" if it is located in the subfolder
# "resources" in the folder this script is in.
# Requires PowerShell 3.0 or better.
. "$PSScriptRoot\resources\library1.ps1"</pre></html>

PowerShell is not just an automation language but also an alternate user interface. If you do not like the graphical interface, educate PowerShell to open the tools you need via easy alias names.
For example, to open the device manager, you could use its original name:
<html><pre>PS> devmgmt.msc
PS></pre></html>
If you do not want to remember this name, use an alias:
<html><pre>PS> PS> Set-Alias -Name DeviceManager -Value devmgmt.msc
PS> DeviceManager
PS> </pre></html>
As you can see, to open the device manager, all you now need is enter "DeviceManager". You can also just enter "Device" and press TAB to use auto-completion.
Aliases will vanish when PowerShell closes, so to keep your aliases, add the Set-Alias command(s) to your profile script. The path can be found in $profile. You may have to create this file (and its parent folders) first if it does not yet exist. Test-Path can check if it is already present or not.
<html><pre>PS> PS> $profile
C:\Users\Tobias\Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1
PS> Test-Path $profile
True</pre></html>

TimeSpan objects represent a given amount of time. They are incredibly useful when you calculate with dates or times because they can represent the amount of time between two dates, or can add a day (or a minute) to a date to create relative dates.
Here are some samples to get you started:
# get a timespan representing one day and 3 hours:
<html><pre>New-TimeSpan -Days 1 -Hours 3</pre></html>
# get a timespan representing the time difference between now and next Christmas
<html><pre>New-Timespan -End '2013-12-24 18:30:00'</pre></html>
# get a timespan by subtracting two dates:
<html><pre>[DateTime]'2013-12-24 18:30:00' - (Get-Date)</pre></html>
# get a timespan by subtracting a timespan representing one day from a date:
<html><pre>(Get-Date) - [TimeSpan]'1.00:00:00'</pre></html>
# getting a specific property from a timespan (for example just the days):
<html><pre>$days = (New-Timespan -End '2013-12-24 18:30:00').Days
"Days to Christmas: $days"</pre></html>
# negating a timespan:
<html><pre>$timespan = New-TimeSpan -Days 1
$timespan.Negate()
$timespan</pre></html>
# creating a negative timespan directly:
<html><pre>New-TimeSpan -Days -1</pre></html>

If you want to write plain text information to a file, don't use Out-File. Instead, use Set-Content. It is much faster.
This example takes WindowsUpdate.log and replaces all instances of "error" with "ALERT", saves the changes to a new file and opens it in your text editor:
<html><pre>$tempfile1 = "$env:temp\tempfile1.txt"
$tempfile2 = "$env:temp\tempfile2.txt"
$tempfile3 = "$env:temp\tempfile3.txt"
$text = Get-Content -Path C:\Windows\WindowsUpdate.log</pre></html>
; see how long it takes to write this with Out-File
<html><pre>Measure-Command {
$text | Out-File -FilePath $tempfile1
}</pre></html>
; see how long it takes to write this with Set-Content
<html><pre>Measure-Command {
$text | Set-Content -Path $tempfile2 -Encoding Unicode
}</pre></html>
; see how long it takes to write this with Set-Content
<html><pre>Measure-Command {
Set-Content -Path $tempfile3 -Encoding Unicode -Value $text
}</pre></html>
Depending on how large the WindowsUpdate.log file is on your machine, you get back varying results. However, Set-Content is more than twice as fast, and if you submit the text to the parameter -Value rather than sending it over the (slow) pipeline, the overall result is even 6x as fast or better.
Use Out-File only if you want to write objects to a file. The primary reason why Out-File is slower is: it tries and converts objects to plain text. Set-Content writes the text as-is - which is all you need if you want to write plain text in the first place.
See also [[Quickly Replace Words in Text FileNew Tiddler]]