Creating Dynamic Variable Content

PowerShell 2+

In the previous tip we introduced variables with read-only dynamic content that updated each time the variable was read. This required a little bit of C# code. With Add-Member, you can get a similar result using script properties:

$info = New-Object -TypeName PSObject
$info | Add-Member -Name Random -MemberType ScriptProperty -Value { Get-Random }
$info | Add-Member -Name Now -MemberType ScriptProperty -Value { Get-Date }

You can now call $info.Random or $info.Now to get a new random number or the current date.

Twitter This Tip! ReTweet this Tip!

Creating Dynamic Variable Content

PowerShell 2+

A little-known fact is that you can add variables to PowerShell that automatically update their content. If you need random numbers, for example, this code would add a new variable called $Random that provides a new random number each time it is called:

Add-Type 'using System;
using System.Management.Automation;
public class FixedVariableRandom : PSVariable
{
Random r;
public FixedVariableRandom()
  : base("Random", 0, ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope)
  {r = new Random();}

  public override object Value
  {
    get { return  r.Next(); }
  }
}'

$ExecutionContext.SessionState.PSVariable.Set((New-Object FixedVariableRandom))

Likewise, this example creates a variable named $Now which always returns the current date and time:

Add-Type 'using System;
using System.Management.Automation;
public class FixedVariableNow : PSVariable
{
public FixedVariableNow()
  : base("Now", 0, ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope)
  {}

  public override object Value
  {
    get { return  DateTime.Now; }
  }
}'

$ExecutionContext.SessionState.PSVariable.Set((New-Object FixedVariableNow))

Twitter This Tip! ReTweet this Tip!

Create Simple PowerShell Host

PowerShell 2 or better

There are sometimes a lot of good reasons why you’d like to run a script by a different host. powershell.exe might be blocked by an AppLocker policy, or you’d like to run a PowerShell script invisible, without displaying a console.

Any PowerShell host can execute PowerShell scripts, and thanks to Add-Type, it is easy to use PowerShell to compile such a host. When you run this code, you end up with a new console application called PSHost.exe on your desktop:

# this is the C# code for a simple PowerShell host
$code = @'
using System.Management.Automation;  
using System.Collections.ObjectModel;

namespace PowerSaw {  
  class Program {
    static void Main(string[] args) {
      if (args.Length == 0) return;
      using (PowerShell ps = PowerShell.Create()) {
        string code = System.IO.File.ReadAllText(args[0]);
        ps.AddScript(code);
        Collection<PSObject> psobject = ps.Invoke();
      }
    }
  }
}
'@

# parameters for compilation to a console EXE
$params = @{
  OutputAssembly = "$homeDesktopPSHost.exe"
  OutputType = 'ConsoleApplication'
  ReferencedAssemblies = [PSObject].Assembly.Location
  TypeDefinition = $Code
}

# create an EXE file
Add-Type @params

# show EXE file
explorer.exe /select,$params.OutputAssembly

Now, you can use this simple host to run arbitrary PowerShell scripts. Simply pass the script path as argument to the EXE. Here is an example (assuming that you ran the above code and have PSHost.exe on your desktop):

$Desktop = "$homedesktop"
$ScriptPath = "$Desktopgetservices.ps1"
'Get-Service | Out-GridView -Wait' | Out-File -FilePath $ScriptPath -Encoding UTF8

& "$Desktoppshost.exe" $ScriptPath

Note that the simple PowerShell host created in this example has no output logic. So use it to call PowerShell scripts that just do things, or pipe the results to some output platform such as Out-GridView. Note also that the host will close as soon as the script is done.

Twitter This Tip! ReTweet this Tip!

Creating PowerShell Web Server

All Versions

Here is an example that implements a simple web server with just a few lines of code. When you run this script, you can enter one of these URLs into any web browser on your machine:

http://localhost:8080/

http://localhost:8080/services

And here is the code:

# enter this URL to reach PowerShell’s web server
$url = 'http://localhost:8080/'

# HTML content for some URLs entered by the user
$htmlcontents = @{
  'GET /'  =  '<html><body>Here is PowerShell</body></html>'
  'GET /services'  =  Get-Service | ConvertTo-Html

}

# start web server
$listener = New-Object System.Net.HttpListener
$listener.Prefixes.Add($url)
$listener.Start()

try
{
  while ($listener.IsListening) {  
    # process received request
    $context = $listener.GetContext()
    $Request = $context.Request
    $Response = $context.Response

    $received = '{0} {1}' -f $Request.httpmethod, $Request.url.localpath
    
    # is there HTML content for this URL?
    $html = $htmlcontents[$received]
    if ($html -eq $null) {
      $Response.statuscode = 404
      $html = 'Oops, the page is not available!'
    } 
    
    # return the HTML to the caller
    $buffer = [Text.Encoding]::UTF8.GetBytes($html)
    $Response.ContentLength64 = $buffer.length
    $Response.OutputStream.Write($buffer, 0, $buffer.length)
    
    $Response.Close()
  }
}
finally
{
  $listener.Stop()
}

Please note that the listener runs in an endless loop. When you abort the script, it takes another web request from a web browser for PowerShell to return to you.

Twitter This Tip! ReTweet this Tip!

Changing Password

All versions

Here is a simple way how an existing user can change the password for his account. The user must know his old password and does not need Administrator privileges. Make sure you change the user details at the beginning of this script:

$username = 'yourUserName'
$domain = 'test.lab'

$oldPassword = 'OldPassword'
$newPassword = 'NewPassword'

$sourceCode = @'
[DllImport("netapi32.dll", CharSet = CharSet.Unicode)]
public static extern bool NetUserChangePassword(string domain, string username, string oldpassword, string newpassword);  
'@

Add-Type -MemberDefinition $sourceCode -Name 'Api' -Namespace 'Win32'

[Win32.API]::NetUserChangePassword($domain, $username, $oldPassword, $newPassword)

Twitter This Tip! ReTweet this Tip!

Opening PowerShell for Folder

Any version

If you browsed to a folder using File Explorer, and then would like to open a PowerShell with the current folder set to the browsed folder, just click the address bar in File Explorer, and replace the path with the “powershell” command.

This only fails if the folder called “powershell” exists, in which case File Explorer will switch to this folder rather than opening a PowerShell console.

You can also enter “powershell_ise” into the File Explorer address bar to open the PowerShell ISE with the current folder set as default.

Twitter This Tip! ReTweet this Tip!