Archive for January, 2010

PowerGUI MobileShell Team on PowerScripting Podcast

Tonight at 9:30 pm EST Darin Pendergraft, Kirk Munro and myself will be there on Hal’s and Jonathan’s PowerScripting Podcast.

If you are not asleep at that time, please join the live video stream and ask any questions, share thoughts and so on. Also, this is the 100th episode of the show – so plenty of cool stuff to be shared!

See you in a few hours!

New in PowerGUI 2.0: Snapshots

Our free PowerShell tool just got its version 2.0 release and besides multiple bugfixes and improvements a great new feature: Configuration Snapshots.

The way snapshots work is really similar to what you find in virtualization tools. At any time you can create a Snapshot and this will effectively make PowerGUI Admin Console save a copy of the current state of all your nodes, actions, PowerPacks and so on so if you mess up with your experiments you have a checkpoint to which you can revert:

Besides that we automatically create a new snapshot each time you install or upgrade a PowerPack so you have an ability to roll these changes back.

But wait, there is more!

You can select any two snapshots and view a detailed report on what exactly changed between them:

Or even open a previous snapshot in PowerGUI Admin Console side-by-side with the current state:

We hope this makes your experience on working with PowerGUI Admin Console and authoring (and using) PowerPacks much more enjoyable!

You can read about some of bugfixes based on community feedback in our release notes.

Download PowerGUI 2.0 and let us know what you think!

Select-Object with Subproperties

Get-Process | Select ProcessName, StartTime.DayOfWeek” – if you ever wanted this notation to work to give you subproperties of objects – read on. ūüôā

This short notation for subproperties (in this particular case:¬† StartTime.DayOfWeekto get the DayOfWeek property of process start time) is unfortunately not supported in PowerShell out of the box. Instead you have to type something like: “@{Name=”Start Day”; Expression = {$_.StartTime.DayOfWeek}}” which is kind of long and scary.

Luckily PowerShell 2.0 has so called proxy functions and I have just implemented my overload of Select-Object which add the ability to directly reference subproperties as in the example above. You can find the code at PoshCode here.

Frankly, using PowerShell 2.0 code snippet for proxy functions this was quite trivial to implement. All I have to do was to check whether any Property parameters have dots, and if they do – substitute them with the long and scary expression notation. ūüôā

In the code below, everything is default output from the snippet except for a dozen of lines after: “#region: Dmitry Sotnikov: substitute dotted properties with expressions

Here’s the code you need to load in your PowerShell session to enhance the Select-Object behavior:

function Select-Object {
[CmdletBinding(DefaultParameterSetName='DefaultParameter')]
param(
  [Parameter(ValueFromPipeline=$true)]
  [System.Management.Automation.PSObject]
  ${InputObject},

  [Parameter(ParameterSetName='DefaultParameter', Position=0)]
  [System.Object[]]
  ${Property},

  [Parameter(ParameterSetName='DefaultParameter')]
  [System.String[]]
  ${ExcludeProperty},

  [Parameter(ParameterSetName='DefaultParameter')]
  [System.String]
  ${ExpandProperty},

  [Switch]
  ${Unique},

  [Parameter(ParameterSetName='DefaultParameter')]
  [ValidateRange(0, 2147483647)]
  [System.Int32]
  ${Last},

  [Parameter(ParameterSetName='DefaultParameter')]
  [ValidateRange(0, 2147483647)]
  [System.Int32]
  ${First},

  [Parameter(ParameterSetName='DefaultParameter')]
  [ValidateRange(0, 2147483647)]
  [System.Int32]
  ${Skip},

  [Parameter(ParameterSetName='IndexParameter')]
  [ValidateRange(0, 2147483647)]
  [System.Int32[]]
  ${Index})

begin
{
 try {
     $outBuffer = $null
     if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
     {
         $PSBoundParameters['OutBuffer'] = 1
     }

     #region: Dmitry Sotnikov: substitute dotted properties with expressions
     if ($Property -ne $null) {
      # Iterate through properties and substitute those with dots
      $NewProperty = @()
      foreach ( $prop in $Property ) {
       if ($prop.GetType().Name -eq 'String') {
        if ($prop.Contains('.')) {
         [String] $exp = '$_.' + $prop
         $prop = @{Name=$prop; Expression = {Invoke-Expression ($exp)}}
        }
       }
       $NewProperty += $prop
      }
      $PSBoundParameters['Property'] = $NewProperty
     }
     #endregion

     $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Select-Object',
         [System.Management.Automation.CommandTypes]::Cmdlet)
     $scriptCmd = {& $wrappedCmd @PSBoundParameters }
     $steppablePipeline =
         $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
     $steppablePipeline.Begin($PSCmdlet)
 } catch {
     throw
 }
}

process
{
    try {
        $steppablePipeline.Process($_)
    } catch {
        throw
    }
}

end
{
    try {
        $steppablePipeline.End()
    } catch {
        throw
    }
}
<#

.ForwardHelpTargetName Select-Object
.ForwardHelpCategory Cmdlet

#>
}

Running MobileShell on Windows 7

While only server versions of Windows (2003, 2008, 2008 R2) are officially supported as PowerGUI Pro MobileShell hosts in production use, if you just want to give it a quick try your Windows 7 laptop is perfectly fine. Here’s what you need to do:

1. Open the Windows Features dialog box,

2. Turn on Internet Information Services,

3. Then browse into World Wide Web Services / Security and select Basic Authentication:

4. Click OK to close the dialog box.

5. Start MobileShell setup.

6. On the feature selection step provide the license (license file available from here).

7. When you click Next, read the warning telling you that running the web site on Windows 7 is a bad idea and click Yes. ūüôā

8. Follow the setup for other settings (they are pretty straight-forward).

9. If you are sure to not ever use this site for real administration you can even select to use HTTP (instead of HTTPS) binding – this saves you from providing site certificate, but obviously is a bad idea for production because your traffic will go unencrypted.

10. When you click Finish you will get the browser launched for the newly installed MobileShell site and can start having some serious PowerShell fun:

Hope this helps you check out MobileShell and get excited enough to later spend time on real server deployment. ūüôā

Export-CSV -Append

Here’s the solution for those who need to append rows to existing CSV files (and cannot do that): I have just used PowerShell 2.0 code snippets to create a proxy cmdlet – function which wraps standard Export-CSV cmdlet but adds handling of the -Append parameter.

So you can do something like:

Get-Process | Export-Csv -Path 'c:\Temp\processes.csv' -Append -Delimiter ';'

As you can see, other parameters – such as Delimiter – still function as well. If the file does not exist – the cmdlet will essentially ignore -Append and create the file as normal. If you specify -Append and the file is present, the function will turn the objects into CSV strings, remove the first row with the property names and append to the existing file.

For your convenience, I have posted the source code to PoshCode. Here’s it is as well for those interested:

#Requires -Version 2.0

<#
  This Export-CSV behaves exactly like native Export-CSV
  However it has one optional switch -Append
  Which lets you append new data to existing CSV file: e.g.
  Get-Process | Select ProcessName, CPU | Export-CSV processes.csv -Append
  
  For details, see
  https://dmitrysotnikov.wordpress.com/2010/01/19/export-csv-append/
  
  (c) Dmitry Sotnikov  
#>

function Export-CSV {
[CmdletBinding(DefaultParameterSetName='Delimiter',
  SupportsShouldProcess=$true, ConfirmImpact='Medium')]
param(
 [Parameter(Mandatory=$true, ValueFromPipeline=$true,
           ValueFromPipelineByPropertyName=$true)]
 [System.Management.Automation.PSObject]
 ${InputObject},

 [Parameter(Mandatory=$true, Position=0)]
 [Alias('PSPath')]
 [System.String]
 ${Path},
 
 #region -Append (added by Dmitry Sotnikov)
 [Switch]
 ${Append},
 #endregion 

 [Switch]
 ${Force},

 [Switch]
 ${NoClobber},

 [ValidateSet('Unicode','UTF7','UTF8','ASCII','UTF32',
                  'BigEndianUnicode','Default','OEM')]
 [System.String]
 ${Encoding},

 [Parameter(ParameterSetName='Delimiter', Position=1)]
 [ValidateNotNull()]
 [System.Char]
 ${Delimiter},

 [Parameter(ParameterSetName='UseCulture')]
 [Switch]
 ${UseCulture},

 [Alias('NTI')]
 [Switch]
 ${NoTypeInformation})

begin
{
 # This variable will tell us whether we actually need to append
 # to existing file
 $AppendMode = $false
 
 try {
  $outBuffer = $null
  if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
  {
      $PSBoundParameters['OutBuffer'] = 1
  }
  $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Export-Csv',
    [System.Management.Automation.CommandTypes]::Cmdlet)
        
        
 #String variable to become the target command line
 $scriptCmdPipeline = ''

 # Add new parameter handling
 #region Dmitry: Process and remove the Append parameter if it is present
 if ($Append) {
  
  $PSBoundParameters.Remove('Append') | Out-Null
    
  if ($Path) {
   if (Test-Path $Path) {        
    # Need to construct new command line
    $AppendMode = $true
    
    if ($Encoding.Length -eq 0) {
     # ASCII is default encoding for Export-CSV
     $Encoding = 'ASCII'
    }
    
    # For Append we use ConvertTo-CSV instead of Export
    $scriptCmdPipeline += 'ConvertTo-Csv -NoTypeInformation '
    
    # Inherit other CSV convertion parameters
    if ( $UseCulture ) {
     $scriptCmdPipeline += ' -UseCulture '
    }
    if ( $Delimiter ) {
     $scriptCmdPipeline += " -Delimiter '$Delimiter' "
    } 
    
    # Skip the first line (the one with the property names) 
    $scriptCmdPipeline += ' | Foreach-Object {$start=$true}'
    $scriptCmdPipeline += '{if ($start) {$start=$false} else {$_}} '
    
    # Add file output
    $scriptCmdPipeline += " | Out-File -FilePath '$Path'"
    $scriptCmdPipeline += " -Encoding '$Encoding' -Append "
    
    if ($Force) {
     $scriptCmdPipeline += ' -Force'
    }

    if ($NoClobber) {
     $scriptCmdPipeline += ' -NoClobber'
    }   
   }
  }
 } 
  

  
 $scriptCmd = {& $wrappedCmd @PSBoundParameters }
 
 if ( $AppendMode ) {
  # redefine command line
  $scriptCmd = $ExecutionContext.InvokeCommand.NewScriptBlock(
      $scriptCmdPipeline
    )
 } else {
  # execute Export-CSV as we got it because
  # either -Append is missing or file does not exist
  $scriptCmd = $ExecutionContext.InvokeCommand.NewScriptBlock(
      [string]$scriptCmd
    )
 }

 # standard pipeline initialization
 $steppablePipeline = $scriptCmd.GetSteppablePipeline(
        $myInvocation.CommandOrigin)
 $steppablePipeline.Begin($PSCmdlet)
 
 } catch {
   throw
 }
    
}

process
{
  try {
      $steppablePipeline.Process($_)
  } catch {
      throw
  }
}

end
{
  try {
      $steppablePipeline.End()
  } catch {
      throw
  }
}
<#

.ForwardHelpTargetName Export-Csv
.ForwardHelpCategory Cmdlet

#>

}

Hope this helps! ūüėČ

MobileShell Demo

Here’s a quick recorded walkthrough of our in-browser PowerShell prompt in case you want to have a look at it before you install it yourself:

Once you watch it and get excited, give it a try! Download PowerGUI MobileShell beta, install it on a Windows Server and let us know what you think!

MobileShell SDK

Developers, here’s your chance to create exciting applications on top of PowerGUI MobileShell server backend! That’s right: everything you see in MobileShell including settings dialog boxes and intellisense is exposed as web services. Your application will be able to execute any PowerShell scripts in production environment over HTTP/HTTPS, preserve session state and even get meta information such as intellisense.

Which means that if you don’t like our in-browser implementation and want to develop a native iPhone/Blackberry/Symbian/Android/Windows Mobile client – you can quite easily do that (and probably even make some money out of the respective app stores ;))

Below is a sample in C# that uses the PowerGUI MobileShell API to:

  • Create, list, join and close sessions
  • Work with IntelliSense autocompletion lists
  • Manage favorite scripts

Hopefully it is straightforward enough to figure out how to perform similar calls in your development platform of choice.

For this particular example to work in Visual Studio, you need to add the MobileShell Web service as a Web reference to the .NET project you are working with. The URL of the service looks like: https://<MobileShell_server_name>/MobileShell/MobileShell/MobileShellWebService.asmx.

For details about adding Web references to Visual Studio, see http://msdn.microsoft.com/en-us/library/d9w023sx.aspx.

If you are using tools other than Visual Studio to run .NET code against the MobileShell Web service, you need to generate a Web service proxy using the wsdl.exe utility, which is distributed with .NET Framework 3.5. For details, refer to the help screen for the utility.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MobileShellExamples
{
 class Program
 {
  static void Main(string[] args)
  {
    ScriptExecutionExample();
    FavoritesExample();
    IntelliSenseExample();
    SessionsExample();
  }

  static void ScriptExecutionExample()
  {
   MobileShellWebService webService = GetWebServiceInstance();
   webService.CreateSession();

   string strScript = "while ( $true ) { $i++; \"Hello $i\"; sleep -m 300 }";
   ScriptState state = webService.ExecuteScript(strScript);

   System.Threading.Thread.Sleep(5000);

   webService.TerminateCurrentScript();

   state = webService.GetCurrentScriptState();
   Console.WriteLine((state.Command as WriteCommand).Text);
  }

  static void SessionsExample()
  {
   // Create the first session
   MobileShellWebService webService1 = GetWebServiceInstance();
   webService1.CreateSession();

   // Create the second session
   MobileShellWebService webService2 = GetWebServiceInstance();
   webService2.CreateSession();

   // Get a list of active sessions
   MobileShellWebService webService3 = GetWebServiceInstance();
   SessionDetails[] arrSessionIDs = webService3.GetSessions();

   // Close all sessions except the first
   for (int i = 1; i < arrSessionIDs.Length; i++)
   {
    webService3.CloseSession(arrSessionIDs[i].SessionID);
   }
   // Join the first session
   webService3.JoinSession(arrSessionIDs[0].SessionID);
  }

  static void IntelliSenseExample()
  {
   MobileShellWebService webService = GetWebServiceInstance();

   // Get autocompletion list for the "Get-" string (e. g. get all Get-* cmdlets)
   string strScript = "Get-";
   foreach (IntelliSenseItem item in
           webService.GetIntelliSenseCompletionList(strScript, strScript.Length-1))
   {
    Console.WriteLine(item.AutoCompleteEnding);
   }
   Console.WriteLine("");

   // Get autocompletion list for "Get-Help -" string.
   // (e.g. get all Get-Help cmdlet parameters)
   strScript = "Get-Help -";
   foreach (IntelliSenseItem item in
           webService.GetIntelliSenseCompletionList(strScript,strScript.Length-1))
   {
    Console.WriteLine(item.AutoCompleteEnding);
   }
  }

 static void FavoritesExample()
 {
   MobileShellWebService webService = GetWebServiceInstance();
   // List current favorites
   Console.WriteLine("### Initial list");
   foreach (FavoriteScript cmdFavorite in webService.GetFavoriteScripts())
   {
     Console.WriteLine("Name: " + cmdFavorite.Name);
     Console.WriteLine("Script: " + cmdFavorite.Script);
     Console.WriteLine("");
   }

   // Add a new favorite script named "Hello World"
   webService.AddFavoriteScript("Hello World", "\"Hello World!\"");

   // List current favorites
   Console.WriteLine("### New favorite script added");
   foreach (FavoriteScript cmdFavorite in webService.GetFavoriteScripts())
   {
    Console.WriteLine("Name: " + cmdFavorite.Name);
    Console.WriteLine("Script: " + cmdFavorite.Script);
    Console.WriteLine("");
   }

   // Find the favorite script named "Hello World"
   FavoriteScript cmdNewScript = webService.GetFavoriteScripts().First(
                                     fav => fav.Name == "Hello World");

   // Modify the script name and content
   webService.EditFavoriteScript(cmdNewScript.Name,
                                 "Time keeper", cmdNewScript.Script);

   // List current favorites
   Console.WriteLine("### Favorite script modified");
   foreach (FavoriteScript cmdFavorite in webService.GetFavoriteScripts())
   {
    Console.WriteLine("Name: " + cmdFavorite.Name);
    Console.WriteLine("Script: " + cmdFavorite.Script);
    Console.WriteLine("");
   }
  }

  static MobileShellWebService GetWebServiceInstance()
  {
   return GetWebServiceInstance(
       "http://myserver/MobileShell/MobileShell/MobileShellWebService.asmx",
       "mydomain\\administrator", "=1qwerty");
  }

  static MobileShellWebService GetWebServiceInstance(string strUrl,
                                         string strUser, string strPassword)
  {
   MobileShellWebService webService = new MobileShellWebService();
   webService.CookieContainer = new System.Net.CookieContainer();
   webService.Url = strUrl;
   webService.Credentials = new System.Net.NetworkCredential(strUser, strPassword);
   return webService;
  }
 }
}

Detailed SDK documentation (all methods and classes) can be found in the “PowerGUI Pro MobileShell 2.0 SDK” document posted here.

Give a try and let us know what you think. And obviously whichever apps you create we will be extremely glad to promote them whichever way we can!


My Recent Tweets

Legal

The posts on this blog are provided ‚Äúas is‚ÄĚ with no warranties and confer no rights. The opinions expressed on this site are mine and mine alone, and do not necessarily represent those of my employer - WSO2 or anyone else for that matter. All trademarks acknowledged.

© 2007-2014 Dmitry Sotnikov

January 2010
M T W T F S S
« Dec   Feb »
 123
45678910
11121314151617
18192021222324
25262728293031

%d bloggers like this: