::::::::: PowerShell :::::::::
Thursday, November 30, 2006
  Installing PowerShell with Unattended Windows CD
Here are some steps to include Windows PowerShell(for English Version of Windows XP w/ SP2) in an unattended Windows CD.

I have copied Windows CD content to "C:\xpcd"
Windows PowerShell installation file, WindowsXP-KB926139-x86-ENU.exe, has been copied over to C:\xpcd\$OEM$\$1\install\applications\

  1. Create a batch file, install_applications.cmd (just name it to anything with .bat or .cmd extension) at C:\xpcd\$OEM$\$1\install
  2. Open up Winnt.sif and create a [GuiRunOnce] section and call the batch file created in the previous step.
  3. Simply call PowerShell installation file with silent setup switches in the batch file

... other application installation statements...
ECHO Installing PowerShell
ECHO Please wait...
start /wait %systemdrive%\install\applications\WindowsXP-KB926139-x86-ENU.exe /quiet /passive /norestart

Yes... Now I have a Windows XP CD with Windows PowerShell.
I have installed Windows XP and saw other applications (WinZip, IrfanView, etc...) being installed.
Since PowerShell was to be installed in a silent mode, I didn't expect to see anything happening.
But 10 minutes later, PowerShell wasn't still installed.

Oh yes. I have forgotten to include .NET Framework 2.0 onto the CD...
Well, download .NET 2.0 redistributable(i have placed the installation file under C:\xpcd\$OEM$\$1\install\applications\DotNet\2\dotnetfx.exe).
And you must include the following script BEFORE installing PowerShell so that the result is

ECHO Installing .NET Framework 2.0
ECHO Please wait...
start /wait %systemdrive%\install\applications\DotNet\2\dotnetfx.exe /q:a /c:"install.exe /qb"

ECHO Installing PowerShell
ECHO Please wait...
start /wait %systemdrive%\install\applications\WindowsXP-KB926139-x86-ENU.exe /quiet /passive /norestart

Now PowerShell should install without a problem...

Tags :
Saturday, October 14, 2006
  While Creating a Simple EventLog PsProvider -Part 2
I was finally able to add EntryType dynamic parameter to Get-ChildItem
Been slacking off lately and finally made some time to extend Get-ChildItem cmdlet in EventLog provider.

Now, in the following screenshot, I am in the sys: drive of type EventLog for System EventLog.
Get-ChildItem returns list of all EventLogEntry objects for System EventLog.
Now, I can just filter the output to display according to the EventLogEntryType of each EventLogEntry.

And what's cool is that(well about PowerShell that is, not my provider), if I pass an invalid Enumeration value for EntryType parameter, it displays error message with valid enumeration values Error, Warning, Information, SuccessAudit, FailureAudit

It's PowerShell in the works... not my code that displays valid enum values.

Tags :
Monday, October 09, 2006
  While Creating a Simple EventLog PsProvider (in C#)

 When PowerShell was still called Monad, there was a PDC05 Hands-On Lab document available with Monad distribution.  The document was about "Creating Monad Providers" for Access database.

I have started to go over the old PDC 05 document since I could not find enough documentation to get started.  The document was a bit out-dated but it was not hard to figure out what's been changed since Monad beta 2.


Patterns and utility methods are about the same as what's used for creating AccessDb provider in the lab.  But the problem was that, I wanted to be able to create a provider-specific parameter for my PsProvider. (Let me get back to this a bit later on)

For e.g.).  Under Certificate provider, Get-ChildItem has a Certificate-Provider specific parameter -CodeSigningCert for Get-ChildItem.

Now let's see how one can create a "sys" drive for System EventLog

You can retrieve System event log entries using Get-ChildItem cmdlet(or ls, or dir whichever alias you prefer):

ls sys:

Above is equivalent to:

Get-EventLog -logName System

Both ls sys: and Get-EventLog System returns all event log entries in System EventLog(well as you might or might not have guessed ;)) and not only they display the same content, but the output should look exactly the same. 

The reason that output look exactly the same is because there is a pre-defined display format for the type System.Diagnostics.EventLogEntry in $PsHome/dotnettypes.format.ps1xml.


Now, back to the provider-specific parameter for Get-ChildItem, my implementation takes in Index of event log entry.  But I would also like to supply additional parameters so that Get-Item can retrieve event log entry based on either EventID or InstanceID like the following

Get-Item -EventID 18

Get-Item -InstanceID 198397

Anyways, here is the current implementation of Get-Item in action

(201..205) is an array holding indexes from 201 through 205 so EventLogEntry objects with Indexes between 201 and 205 are displayed.  How neat. I prefer that over

get-eventlog system | ? { ($_.Index -ge 201) -and ($_.Index -le 205) }

since there is no need to specify which log to list event log entries from nor use clunky syntaxes for Index ranges.  Well, one can live with above syntax but when it comes to scattered index ranges you want to select, the latter approach gets a bit too uhm, unpractically complicated.   Try accomplish ls (1..5+10..15) using Get-EventLog, it doesn't look pretty...


Lastly, Xaegr on IRC has suggested that it wouldn't hurt to add a support to get remote machine's EventLog entries.  Xaegr has suggested a UNC path like syntax "\\MachineName\LogName" for the Root string, but I just went with MachineName:LogName for a quick and dirty testing...  Now, this functionality is quite buggy... so when I tried to use IP instead of hostname, I get errors like Get-Item cannot find network path, blah...   Moreover, you should have already impersonated or authenticated to the remote machine before creating new EventLog PsDrive for the remote machine and must have appropriate privileges to read EventLog.  Since I am not systems administrator, this is a bit tough to understand...


Well, I would appreciate it if someone could answer my question on NG(PowerShell News Group)


W00t, My first post using Windows Live Writer... Took me really long to get used to this a bit.



Tags :
Sunday, August 13, 2006
  Add-Member: Extended Example
Add-Member cmdlet provides a way to "Add(s) a user-defined custom member to an object"

There are 3 examples on get-help add-member help file but they do not seem to quite reflect the real power of this cmdlet.

Examples in the offical help document, examples deals with following cases

  1. Adding a NoteProperty to an object

  2. Adding an AliasProperty to an object

  3. Adding a NoteProperty to an object, pass thru the result to a variable

Extended Example
: Add a ScriptProperty for converting From/To a Base64 string of a string

  1. Add "ToBase64String"
    [^_^]PS[217]>$str = "abc" -as [psobject]
    [^_^]PS[218]>$str | Add-Member -MemberType ScriptProperty -Name ToBase64String `
    >> -Value {[System.Convert]::ToBase64String([System.Text.Encoding]::UNICODE.GetBytes($this))}

  2. Add "FromBase64String"
    [^_^]PS[220]>$base64 = $str.ToBase64String
    [^_^]PS[222]>$base64 | Add-Member -MemberType ScriptProperty -Name FromBase64String `
    >> -Value {[System.Text.Encoding]::UNICODE.GetString([System.Convert]::FromBase64String($this))}
Above example shows how to add a new member(ScriptProperty) to easily access a converted Base64 string from string variables
NOTE:-as [psobject] in the first line is necessary since Add-Member works only on the object of type PsObject, so -as [psobject] is added to explicitly convert a string variable to of type PsObject

On 218th statement, We add a script block(We are adding a ScriptProperty, aren't we?) to convert the current string(specified as $this, the current object we are working on) into a Base64 representation
Now retrieving a Base64 bytes of the string is as easy as accesing newly aded Script Property;


On 219~222 statements, we assign the Base64 string to a new variable, $base64 and then add a new ScriptProperty member(FromBase64String) to reverse the process(Converting Base64 string back to orignal string)
We have added the script property the same way as we did for ToBase64String but with different function in the script block.
Now again, you can retrieve the orignal string through From64String


The example is a modified version of PowerShell Team blog entry on http://blogs.msdn.com/powershell/archive/2006/04/25/583265.aspx.
One might think about adding both ToBase64String and FromBase64String on the same variable like the following(yes, i have tried it like hmm not sure how many times...)
[^_^]PS[258]>$str = "abc" -as [PsObject]
[^_^]PS[259]>$str | Add-Member ScriptProperty ToBase64String `
>> {[System.Convert]::ToBase64String([System.Text.Encoding]::UNICODE.GetBytes($this))}
[^_^]PS[260]>$str | Add-Member ScriptProperty FromBase64String `
>> {[System.Text.Encoding]::UNICODE.GetString([System.Convert]::FromBase64String($this))}

But $str.ToBase64String.FromBase64String does not produce any result because $str.ToBase64String is not $str but a string returned as a result of Script Property method.(Would anyone correct me if I got it wrong?)

In the Team Blog Entry, in which talks about extending Type definition to add above script properties, above scenario works since type definition extends System.String type itself

so even $str.ToBase64String.FromBase64String as well as ScriptProperty being permanent(not just available through the current session but "across" sessions)

You can find more examples and ideas to extend on Mow's blog entry, Some fun With Monads Add-Member, MP3 files and COM and http://del.icio.us/powershell/ExtendedTypeSystem

Tags :
Saturday, July 29, 2006
  Back up SQL Server database with SQL Server Objects(SMO) using PowerShell
It is easy to set up SQL Server Agent jobs to backup database in timely fashion.
But why not in PowerShell while SMO is exposed in .NET assembly?

Backup-Database -dbName [string] -deviceNames [string[]] [-deviceType [Microsoft.SqlServer.Management.Smo.DeviceType]] [-Overwrite [Switch]] [[-Differential] [Switch]]


Photobucket - Video and Image Hosting

There are times that you do not know all the device types available,
then simply enter invalid device type
A great feature of PowerShell is that PowerShell will enumerate DeviceType
and kindly give you list of valid device type names when invalid type is entered
[^_^]PS[597]>Backup-Database -dbName:pubs -deviceNames:'d:\bak\pubs.bak' -deviceType:DontKnow
Backup-Database : Cannot convert value "DontKnow" to type "Microsoft.SqlServer.Management.Smo.DeviceType" due to invali
d enumeration values. The possible enumeration values are "LogicalDevice, Tape, File, Pipe, VirtualDevice".
At line:1 char:73
+ Backup-Database -dbName:pubs -deviceNames:'d:\bak\pubs.bak' -deviceType:D <<<<>


  1. Microsoft SQL Server 2005 Management Objects Collection
    : You need to download and install one of SQLServer2005_XMO_.msi versions
  2. How to create $server variable: Refer to Scripting Database Objects using SMO (Updated) on SqlTeam.com

# author: Sung Kim
# Description: Create Full or Differential backups for
# SQL Server database with SQL Server Objects(SMO) using PowerShell
function Backup-Database {
param([string]$dbName = $(throw 'Enter Database name to backup'),
[string[]]$deviceNames = $(throw 'Enter device name(s)'),
[Microsoft.SqlServer.Management.Smo.DeviceType]$deviceType =
[switch]$Overwrite = $false,
[switch]$Differential = $false)

trap { break; }

# Load SMO assembly
'C:\Program Files\Microsoft SQL Server\90\SDK\Assemblies'
"$PRIVATE:SmoDir\Microsoft.SqlServer.Smo.dll") | out-null

# Initialize backup object
$PRIVATE:bkp = New-Object Microsoft.SqlServer.Management.Smo.Backup
$PRIVATE:bkp.Database = $dbName
$PRIVATE:bkp.Initialize = $Overwrite
$PRIVATE:bkp.Incremental = $Differential

# Add backup devices to backup database to
foreach ($PRIVATE:deviceName in $deviceNames) {
$PRIVATE:bkpItem =
new-object Microsoft.SqlServer.Management.Smo.BackupDeviceItem(

if ($Overwrite) {
Write-Warning "Overwriting existing backup set"


if ($Differential) {
Write-Host -NoNewLine -F Blue "Differential Backup: "
Write-Host -NoNewLine "$($dbName) has been backed up to "
Write-Host -ForegroundColor Cyan -NoNewLine "$($deviceNames)"
Write-Host " successfully"

Tags :
Friday, July 14, 2006
  Re-inventing the wheel for fun.
I was going through Community-Submitted Scripts Center on Microsoft TechNet and came across a script named, "BackupFiles.ps1" by Greg Lyon.

The script is functional and works.
But I didn't like the fact that it looked more like a VBScript script than a PowerShell script.
Moreover, the script was heavily utilizing a COM object instance of type "Scripting.FileSystemObject".

My goal was simply to change the code to make it look more PowerShell-like(uhm, this should mean different for different people, but in my case, i tend to use "PowerShell-Like" when a script is written mostly with built-in (cmdlets -and functions)). Nothing less, nothing more.

Re-inventing the wheel for fun.

Modified mostly "function BackupFolder" part

# BackupFiles.ps1
# Greg Lyon - July 2006
# reference: http://www.microsoft.com/technet/scriptcenter/csc/scripts/backup/backup/cscbk013.mspx
# Modified by DBMwS - July 14, 2006

param($strSourceFolder = "C:\programming\ps\test",
$strDestinationFolder = "F:\backup\programming\ps\test")

#{{{ function BackupFolder
function BackupFolder
param([string] $SourceDir = $(throw 'Enter Source Directory Name'),
[string] $DestinationDir = $(throw 'Enter Desitnation Directory Name'))

if (! (test-path $SourceDir)) {
throw "$($SourceDir) is not a valid directory!"
if (! (test-path $DestinationDir)) {
throw "$($DestinationDir) is not a valid directory!"

# Check if destination directory is missing any files in the source
# directory.if any file is missing, copy them over to the backup dir.
get-childitem $SourceDir |
foreach {
if ($_.PsIsContainer) {
$childDstDir = (join-path $DestinationDir $_.Name)
if (! (test-path $childDstDir)) { [void](mkdir $childDstDir) }
BackupFolder $_.FullName $childDstDir
} else {
$srcFile = $_.FullName
$destFile = join-path $DestinationDir $_.Name

if (test-path $destFile) {
$timeDiff = ($srcFile.LastWriteTime - $destFile.LastWriteTime).TotalSeconds
if ($timeDiff -gt 2) {
copy-item $srcFile $destFile -Force
write-host "Copied file $($srcFile) to $($destFile)"
} else {
copy-item $srcFile $destFile -Force
write-host "Copied file $($srcFile) to $($destFile)"

# Check if Source dir is missing any files on the backup dir.
# if there is/are file(s) missing, copy them over from backup dir to src dir
get-childitem $DestinationDir |
foreach {
if ($_.PsIsContainer) {
$childSrcDir = (join-path $SourceDir $_.Name)
if (! (test-path $childSrcDir)) { [void](mkdir $childSrcDir) }
BackupFolder $childSrcDir $_.FullName
} else {
$srcFile = join-path $SourceDir $_.Name
if (! (test-path $srcFile)) {
remove-item $_
write-host "Deleted file $($_)"

#{{{ function WaitKey
function WaitKey {
param( [String] $strPrompt = "Press any key to continue ... ")
Write-Host $strPrompt -NoNewline
$key = [Console]::ReadKey($true)

$iCopyCount = 0
$iDestDeletedCount = 0

Write-Host "Backing up " -NoNewline -ForegroundColor "White"
Write-Host $strSourceFolder -ForegroundColor "Cyan" -NoNewline
Write-Host " to " -NoNewline -ForegroundColor "White"
Write-Host $strDestinationFolder -ForegroundColor "Cyan"

if (! (test-path $strSourceFolder)) {
Write-Host "Error: source folder does not exist!" -ForegroundColor "Red"
Write-Host "Exiting script"
WaitKey "Press any key to exit ... "

if (! (test-path $strDestinationFolder)) {
Write-Host "Warning: destination folder`($($strDestinationFolder)`) does not exist"
$p = Read-Host "Create folder and continue? "

if ( $p[0] -ieq 'y' ) {
new-item -Type Directory -Path $strDestinationFolder | out-null
} else {
Write-Host "Exiting script"
WaitKey "Press any key to exit ... "

BackupFolder $strSourceFolder $strDestinationFolder

if( ($iCopyCount -eq 0) -and ($iDestDeletedCount -eq 0) ) {
Write-Host "Folders are synchronized" -ForegroundColor "magenta"
} else {
Write-Host $iCopyCount "files copied from source to destination" -ForegroundColor "magenta"
Write-Host $iDestDeletedCount "orphaned destination files deleted" -ForegroundColor "magenta"

WaitKey "Press any key to exit ... "


Tags :
Wednesday, June 28, 2006
  Windows Desktop Search Powershell Cmdlet by Sean McLeod
Great stuff!
A cmdlet, Get-WDS (in SnapIn) that searches WDS(Windows Desktop Search) indexes is born...

Mr. Sean McLeod(the author) Newsgroup post on microsoft.public.windows.PowerShell can be found here.

Instead of me explaining what the cmdlet is about and how it works, go to The Code Project article, Windows Desktop Search Powershell Cmdlet

Tags :
Let's get lazy with PowerShell!

Location: Flushing, NY, United States

Experimenting with a different format of blogs...

10/01/2005 - 11/01/2005 / 11/01/2005 - 12/01/2005 / 12/01/2005 - 01/01/2006 / 01/01/2006 - 02/01/2006 / 02/01/2006 - 03/01/2006 / 03/01/2006 - 04/01/2006 / 04/01/2006 - 05/01/2006 / 05/01/2006 - 06/01/2006 / 06/01/2006 - 07/01/2006 / 07/01/2006 - 08/01/2006 / 08/01/2006 - 09/01/2006 / 10/01/2006 - 11/01/2006 / 11/01/2006 - 12/01/2006 /

Powered by Blogger