Thursday, September 27, 2007

Flying to NYC


I'll be spending the nest 10 days in our company's web farm in NYC.
Its going to be an 11 hours flight from Israel to NY :-(

I'm going to install some new web servers and 2 load balancers.

See ya next week, or so :-)

Thursday, September 20, 2007

List databases health with SQL SMO


"Get-Databases" function creates a custom "view" of some database properties on the specified SQL server.


function Get-SQLDatabases{
        [string]$server=$(throw "Please specify SQL server name.")

    $smo = new-object Microsoft.SqlServer.Management.Smo.Server $server

    $smo.databases | foreach {
        $db = $smo.databases[$];
        $obj = new-object psobject;
        add-member -inp $obj NoteProperty "#" ($count+1);
        add-member -inp $obj NoteProperty "Server" ([string][regex]::replace($db.Parent,"\[|\]","")).toupper();
        add-member -inp $obj NoteProperty "DB Name" $db.Name;
        add-member -inp $obj NoteProperty "ID" $;
        add-member -inp $obj NoteProperty "Size(MB)" $db.size.tostring("N2");
        add-member -inp $obj NoteProperty "Free(MB)" ($db.SpaceAvailable/1024).tostring("N2") ;
        add-member -inp $obj NoteProperty "Status" $db.Status;
        add-member -inp $obj NoteProperty "Last Backup" $db.LastBackupDate;
        add-member -inp $obj NoteProperty "SystemDB" $db.IsSystemObject;


PS > Get-SQLDatabases shayl\sqlexpress | format-table -autosize

# Server                     DB Name ID Size(MB) Free(MB) Status  Last Backup               SystemDB
- ------                       -------      -- --------    --------    ------    -----------                   --------
1 SHAYL\SQLEXPRESS master    1  6.75       1.44        Normal  01/01/0001 00:00:00  True
2 SHAYL\SQLEXPRESS model     3  3.19       1.08        Normal  01/01/0001 00:00:00  True
3 SHAYL\SQLEXPRESS msdb      4  9.44       2.00        Normal  01/01/0001 00:00:00  True
4 SHAYL\SQLEXPRESS tempdb   2  2.69      1.03         Normal  01/01/0001 00:00:00  True
5 SHAYL\SQLEXPRESS test        5  7.13       0.20        Normal  01/01/0001 00:00:00  False

Create dummy directory tree


The "New-DummyTree" function creates a directory tree duplicate. The new directory tree will be an exact copy of the source directory, including file names and there's date attributes, only files will be 0 bytes long. Then you can test your scripting scenarios on its structure.

I used it only once when I tested a mass file processing activity (copy, delete, rename, archive (with New-RarArchive) and so on.

I couldn't test it against the real directory tree since it was based on the files date attributes, not to mention that the source files were very big in size, and I wanted to test the script as one piece and not just part of it in separate files.

It also demonstrates how you can alter a file or directory date attributes, such as LastWriteTime, etc.

So, for what it's worth...

function New-DummyTree{
        [string]$source=$(throw "Invalid source directory path"),

    if( -not (Test-Path $source -pathType container)){
        write-error "Invalid source directory path: <$source>";

    get-childitem $source -recurse | where {-not $_.psiscontainer} | foreach {
        $path="{0}{1}" -f $destination,$($_.directoryname.remove(0,$source.length));
        $file=new-item -path $path -name $ -type file -force;

    #set-location $destination


Note, You don't need to create the destination directory, It will be created on the fly if it doesn't exist.


New-DummyTree <SourcePath> <DestinationPath>

Wednesday, September 19, 2007

Windows PowerShell in Practice

There has been some talking on the Windows PowerShell in Action  Manning forum about a new PowerShell  book titled "Windows Powershell in Practice". Windows PowerShell in Action by Bruce Payette was fantastic. Personally, it was and still my PowerShell bible. I'm sure the new companion book will be as exciting as its big brother ;-)

Bruce comments when asked when the book will release:

"Windows PowerShell in Practice" is a new book that is in development at Manning. I'm not sure when it will be available. It will cover things like the PowerShell SDK (writing cmdlets, providers, embedding the PowerShell engine in other applications, etc.) as well as larger, more domain specific examples and advance scripting techniques.

I'm not currently a direct contributor (but I do kibitz on occasion.) Jim Truher (who co-designed the language with me) and MOW are working on this book. I'll ping them and see how it's going...


Monday, September 17, 2007

WinRAR archive/backup function

This is a utility function I'm using in my scripts to create WinRAR archives from directories or files. It returns True/False respectively.

function New-RarArchive{
        [string] $source=$(throw "Invalid source parameter"),   
        [string] $destination=$(throw "Invalid destination parameter"),   
        [string] $name=$(throw "Invalid name parameter"),
        [string] $winrar = "$env:ProgramFiles\WinRAR\WinRAR.exe",
        [switch] $directory

        if( -not (Test-Path $source -pathType container)){
            write-error "Invalid directory path <$source>";

   } else {
        if( -not (Test-Path $source -pathType leaf)){
            write-error "Invalid file path <$source>";

    if( -not (Test-Path $destination -pathType container)){
        write-error "Invalid destination path <$destination>";

    # winrar switches:
    # A - Add specified files and folders to an archive.
    # IBCK -  Minimize WinRAR to tray, runs in the background.
    # Y - Yes will be the default and automatic reply to all queries.
    # R - recurse subfolders.   
    # ILOG[name] - log errors to file

        & $winrar A -IBCK -Y -R "$destination\$name" $("$source\*.*") | out-null
    } else {
        & $winrar A -IBCK -Y -R "$destination\$name" $source | out-null

    if($LASTEXITCODE -match "[01]"){
        # 0 - Successful operation.
        # 1 - Warning. Non fatal error(s) occurred.
        write-host "New archive created <$destination\$name>" -foreground green
    } else {
            2       {write-error "A fatal error occurred."}
            3       {write-error "CRC error occurred when unpacking."}
            4       {write-error "Attempt to modify a locked archive."}
            5       {write-error "Write error."}
            6       {write-error "File open error."}
            7       {write-error "Wrong command line option."}
            8       {write-error "Not enough memory."}
            9       {write-error "File create error."}
            255    {write-error "User break."}


Archive c:\scripts directory to \\server\share

>> New-RarArchive -source "c:\scripts" -destination \\server\share -name "test.rar" -directory


Archive a file

>> New-RarArchive  -source "C:\Scripts\test.exe" -destination "D:\Backup" -name "test.rar"


WinRAR -ibkp switch adds a visual indicator in the windows system tray.


Sunday, September 16, 2007

Make a choice

Remember the old MS-DOS choice command? It prompts the user to make a choice in a batch program by displaying a prompt and pausing for the user to choose from a set of user-option keys.

In PowerShell, you can provide the same mechanism with .NET built-in classes without the need for external files. You can see this method in action on every cmdlet declared with -confirm parameter.

PS > Get-Process s* | Stop-Process -confirm

Are you sure you want to perform this action?
Performing operation "Stop-Process" on Target "services (520)".
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):

The user is presented with "plain old message box kind" prompt, built from a caption, message and number of choices to choose from:

Confirm = Caption
Are you sure you want to... = Message

And the choices:

[Y] Yes
[A] Yes to All
[N] No
[L] No to All
[S] Suspend


In my scripts I used to build this mechanism manually and write to the host the caption/message and choices until I 'found' this while digging on Mighty MoW's PowerTab scripts. The method is also described on MSDN PSHostUserInterface.PromptForChoice.


PS > $host.ui | gm

   TypeName: System.Management.Automation.Internal.Host.InternalHostUserInterface

Name                          MemberType
----                             ----------
Prompt                        Method
PromptForChoice         Method
PromptForCredential    Method


The definition for PromptForChoice is:

System.Int32 PromptForChoice(String caption, String message, Collection`1 choices, Int32 defaultChoice)


So, how can you implement it in your scripts?  Here is my variation:

$caption = "Confirm";
$message = "Are you sure?";
$yes = new-Object System.Management.Automation.Host.ChoiceDescription "&Yes","help";
$no = new-Object System.Management.Automation.Host.ChoiceDescription "&No","help";
$choices = [System.Management.Automation.Host.ChoiceDescription[]]($yes,$no);
$answer = $host.ui.PromptForChoice($caption,$message,$choices,0)

switch ($answer){
    0 {"You entered 0"; break}
    1 {"You entered 1"; break}

write-host "Rest of script goes here..."


Here is the output:

PS C:\Scripts> .\PromptForChoice.ps1

Are you sure?
[Y] Yes  [N] No  [?] Help (default is "Y"): n
You entered 1
Rest of script goes here...
PS >


Note that the special character "&" (ampersand) might be embedded in the label string to indicate that the next character is a "hot key" (for example, "Yes to &All"). The hot key allows the user to quickly set the input focus to this choice.

The default choice is determined by the order/index of the collection variables ($choices). To indicate no default, set defaultChoice to -1:

$choices = [System.Management.Automation.Host.ChoiceDescription[]]($yes,$no);

$yes = 0
$no = 1

$choices = [System.Management.Automation.Host.ChoiceDescription[]]($yes,$no,$retry);

$yes = 0
$no = 1
$retry = 2


There is also the "Help" option, which we didn't explicitly define.
The strings "help" in the $yes and $no variables are the help text to display when the user types "?"

$no = new-Object System.Management.Automation.Host.ChoiceDescription "&No","help";


I also wanted to implement the Suspend feature. Suspending the process is totally awesome. You have another chance to pause operation and get up to date, verified information, before proceeding. I knew that there is something called "Nested Prompt" and something inside felt that this is the place where it might be used. So I made some tests and here's the code with Suspend.

$caption = "Confirm";
$message = "Are you sure?";
$yes = new-Object System.Management.Automation.Host.ChoiceDescription "&Yes","help";
$no = new-Object System.Management.Automation.Host.ChoiceDescription "&No","help";
$Suspend = new-Object System.Management.Automation.Host.ChoiceDescription "&Suspend","help";
$choices = [System.Management.Automation.Host.ChoiceDescription[]]($yes,$no,$suspend);
$Answer = $host.ui.PromptForChoice($caption,$message,$Choices,0)

switch ($Answer){

    0 {"You entered 0"; break}
    1 {"You entered 1"; break}

    2 {"You entered 2, entering nested prompt, type exit to return"; $Host.EnterNestedPrompt()}   

write-host "Rest of script goes here..."


Here is the output:


PS C:\Scripts> .\PromptForChoice.ps1

Are you sure?
[Y] Yes  [N] No  [S] Suspend  [?] Help (default is "Y"): s
You entered 2, entering nested prompt, type exit to return
PS >>> exit
Rest of script goes here...
PS >

Note that the PowerShell prompt is visually changing, colored in yellow ( adding >> to the default prompt) indicating that you're inside a nested prompt. To exit nested prompts simply type exit.

The Shortcut Guide to Exchange Server 2007 Storage Systems

New complete eBook : The Shortcut Guide to Exchange Server 2007 Storage Systems, by Jim McBee, is available for download on Realtime Publishers website. This web site is a great resource of free eBooks covering all aspects of IT. Register now and download it.

Here's the description for this title:

The conventional way of planning for Exchange Server storage is to throw a lot of disk storage at the server and hope that is sufficient. Mail storage requirements continue to grow for even average users; these users place more and more capacity requirements not only on disk storage but also on disk I/O capacity. As current capacity requirements are exceeded, the storage system must provide scalability. The Shortcut Guide to Exchange Server 2007 Storage Systems, authored by Microsoft Exchange MVP Jim McBee, covers Exchange Server storage capacity requirement planning, the basics of using iSCSI SANs, and best practices for scalability and SAN operations.

Saturday, September 15, 2007

What's the Time around the world?

By scraping TimeAndDate website I could build the World Clock function. If you look on the page HTML source you can find the following for each place:

<a href="city.html?n=110">Jerusalem</a> *</td><td class="r">Sat 5:44 PM</td>

Based on that line we can build a regular expression to match each place and its local time (yellowed text).


Regex break through:

'href="city.html\?n=\d+">(?<place>[^/]*)</a>\s?\*?</td><td class="r">(?<time>[^/]*)</td>'

Starts with href="city.html
Followed by ?n=  (The ? sign is a meta character so escape it, \?n=)
Followed by one or more digits \d+"> with a quote and a closing tag bracket (110)
Followed by (?<place>[^/]*) (equals to Jerusalem, match and capture)
Followed by closing a tag </a>, and (?:\s\*)? (optional space and *, don't insert into $matches)
Followed by </td><td class="r">
Followed by (?<time>[^/]*) (equals to Sat 5:44 PM, match and capture)
Followed by </td>

Captures legend:
(exp)               Match exp and capture it in an automatically numbered group
(?<name>exp)  Match exp and capture it in a group named name
(?:exp)            Match exp, but do not capture it



function Get-WorldClock{

    param (
$baseurl =

# define values and shortcuts for -continent parameter

    "as"                {$url+="$baseurl/custom.html?continent=asia"; break}
    "asia"             {$url+="$baseurl/custom.html?continent=asia"; break}
    "af"                {$url+="$baseurl/custom.html?continent=africa"; break}
    "africa"           {$url+="$baseurl/custom.html?continent=africa"; break}
    "na"                {$url+="$baseurl/custom.html?continent=namerica"; break}
    "namerica"      {$url+="$baseurl/custom.html?continent=namerica"; break}
    "sa"                {$url+="$baseurl/custom.html?continent=samerica"; break}
    "samerica"       {$url+="$baseurl/custom.html?continent=samerica"; break}
    "au"                {$url+="$baseurl/custom.html?continent=australasia"; break}
    "australasia"    {$url+="$baseurl/custom.html?continent=australasia"; break}
    "pa"                {$url+="$baseurl/custom.html?continent=australasia"; break}
    "pacific"          {$url+="$baseurl/custom.html?continent=australasia"; break}
    "eu"                {$url+="$baseurl/custom.html?continent=europe"; break}
    "europe"          {$url+="$baseurl/custom.html?continent=europe"; break}
    default            {$url=$baseurl; break}

trap{throw "Error: Can't connect to $url`n $_"; break}
    $wc=(new-object net.webclient).DownloadString($url);
    $regex=([regex]href="city.html\?n=\d+">(?<place>[^/]*)</a>(?:\s\*)?</td><td class="r">(?<time>[^/]*)</td>').matches($wc);
$times = $regex | select  @{n="Place";e={$_.groups['place'].value}}, @{n="Time";e={$_.groups['time'].value}}
if($filter -eq "*"){
        $times | sort place | format-table -autosize
    } else {
        $times | where {$ -like "*$filter*"} | sort place | format-table -autosize



You can also download the script file from here.



1. Current local times around the world (Main list)

PS > Get-WorldClock

Place               Time
-----                ----
Addis Ababa    Sat 6:01 PM
Adelaide         Sun 12:31 AM
Aden              Sat 6:01 PM
Algiers           Sat 4:01 PM
Amman          Sat 6:01 PM
Amsterdam     Sat 5:01 PM
Anadyr           Sun 4:01 AM
Anchorage      Sat 7:01 AM
Ankara           Sat 6:01 PM
Antananarivo   Sat 6:01 PM
Asuncion         Sat 11:01 AM
Athens            Sat 6:01 PM
Atlanta           Sat 11:01 AM
Auckland        Sun 3:01 AM


2. Current local times in Europe (eu or europe)

PS > Get-WorldClock -continent eu

Place               Time
-----                ----

Beijing            Sat 11:32 PM
Beirut              Sat 6:32 PM
Belgrade          Sat 5:32 PM
Berlin              Sat 5:32 PM
Bogota            Sat 10:32 AM
Boston            Sat 11:32 AM
Brasilia           Sat 12:32 PM
Brisbane          Sun 1:32 AM
Brussels           Sat 5:32 PM
Bucharest        Sat 6:32 PM
Budapest         Sat 5:32 PM
Buenos Aires   Sat 12:32 PM


2. Current local time in Jerusalem (Asia)

PS > Get-WorldClock -continent asia -filter jeru

Place           Time
-----            ----
Jerusalem   Sat 6:42 PM

Exploring Get-Command


I removed some definition columns for readability. During Get-Command exploration I found some interesting things on my environment. Check yours :-)


To get a cmdlet syntax, not so readable


PS > get-command get-childitem -syntax

Get-ChildItem [[-Path] <String[]>] [[-Filter] <String>] [-Include <String[]>] [-Exclude <String[]>] [-Recurse] [-Force] [-Name] [-Verbose]
[-Debug] [-ErrorAction <ActionPreference>] [-ErrorVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Get-ChildItem [-LiteralPath] <String[]> [[-Filter] <String>] [-Include <String[]>] [-Exclude <String[]>] [-Recurse] [-Force] [-Name] [-Verb
ose] [-Debug] [-ErrorAction <ActionPreference>] [-ErrorVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]


Same goes here . It is better to run "get-help cmdlet-name -full"

PS C:\Scripts> get-command get-childitem -definition

CommandType  Name                     Definition
-----------          ----                        ----------
Cmdlet              Get-ChildItem         Get-ChildItem [[-Path] <String[]>] [[-Filter] <String>] [-Include <String[]>] [-Exclude <String[]>] [-Recurse]...



Get the most popular cmdlets by verb. And the winner is...

PS >  get-command | group verb

Count Name
----- ----
  134 Get
   87 Set
   60 Remove
   49 New
   17 Enable
   16 Add
   15 Disable
   15 Test
   11 Update
   10 Export
    8 Move
    7 Write
    6 Out
    6 Start
    5 Stop
    5 Suspend
    5 Resume
    5 Clear
    5 Import
    4 Format
    3 Copy



List commands were "email" appears in the command noun part

PS >  get-command -noun *email*

CommandType     Name
-----------             ----
Cmdlet                 Get-EmailAddressPolicy
Cmdlet                 New-EmailAddressPolicy
Cmdlet                 Remove-EmailAddressPolicy
Cmdlet                 Set-EmailAddressPolicy
Cmdlet                 Update-EmailAddressPolicy


The -commandType property can take one (or more of this values)




So, get all registered filters

PS > get-command -commandType Filter

CommandType Name                       Definition
-----------         ----                          ----------
Function          A:                            Set-Location A:
Function          B:                            Set-Location B:
Function          backup-profile          copy-item $PROFILE "C:\Scripts\PoSH Profile" -force;...
Function          C:                            Set-Location C:
Function          Clear-Host                $spaceType = [System.Management.Automation...
Function          Clear-Temp              remove-item -r -fo $env:temp\* -ea 0;...
Function          D:                            Set-Location D:
Function          e                              & "explorer.exe" $args[0];
Function          E:                             Set-Location E:
Function        echo-args                   for($i=0;$i -lt $args.length;$i++){...
Function        edit-profile                 & $textpad $PROFILE;
Function        F:                               Set-Location F:
Function        G:                                Set-Location G:
Function        get-def                        ($args[0] | gm $args[1]).definition.split(',');...
Function        Get-Params                  param(...
Function        Get-RemoteEventLog    [System.Diagnostics.Eventlog]::GetEventLogs(...
Function        Get-RemoteProcess      [System.Diagnostics.Process]::GetProcesses(...
Function        Get-RemoteService       [void][System.Reflection.Assembly]::LoadWithPartial...


To return all scripts that are found in your PATH environment variable. Interesting ... I installed the Exchange 2007 management pack and it registered some scripts:


PS > get-command -commandType ExternalScript

CommandType    Name                                  
-----------            ----                                  
ExternalScript     AddReplicaToPFRecursive.ps1           
ExternalScript     AddUsersToPFRecursive.ps1             
ExternalScript     CheckInvalidRecipients.ps1            
ExternalScript     ConfigureAdam.ps1                     
ExternalScript     configure-SMBIPsec.ps1                
ExternalScript     enable-CrossForestConnector.ps1       
ExternalScript     Exchange.ps1                          


-CommandType can also get multiple types, separated by a comma


PS C:\Scripts> Get-Command -commandType Filter,ExternalScript

CommandType     Name
-----------             ----
Function              A:
ExternalScript     AddReplicaToPFRecursive.ps1
ExternalScript     AddUsersToPFRecursive.ps1
Function             B:
Function             backup-profile
Function            C:
ExternalScript    CheckInvalidRecipients.ps1
Function           Clear-Host
Function           Clear-Temp
ExternalScript   ConfigureAdam.ps1
ExternalScript  configure-SMBIPsec.ps1
Function          D:
Function          e
Function          E:
Function          echo-args



Now, Get ExternalScript file paths:

PS > get-command -commandType ExternalScript | foreach {$_.path}

C:\Program Files\Microsoft\Exchange Server\Scripts\AddReplicaToPFRecursive.ps1
C:\Program Files\Microsoft\Exchange Server\Scripts\AddUsersToPFRecursive.ps1
C:\Program Files\Microsoft\Exchange Server\Scripts\CheckInvalidRecipients.ps1
C:\Program Files\Microsoft\Exchange Server\Scripts\ConfigureAdam.ps1
C:\Program Files\Microsoft\Exchange Server\Scripts\configure-SMBIPsec.ps1



To add your own scripts directory type (e.g., c:\scripts)

PS > $env:path+=";c:\scripts"


Now you can execute all scripts in your scripts directory, typing only its name (with/out .ps1 extension) and you don't need to be in the scripts working directory. I'll introduce Get-WorldClock on my next blog post :-) 


PS D:\> Get-WorldClock

Place                  Time
-----                   ----
Addis Ababa       Sat 4:19 PM
Adelaide            Sat 10:49 PM
Aden                 Sat 4:19 PM
Algiers              Sat 2:19 PM
Amman             Sat 4:19 PM
Amsterdam       Sat 3:19 PM
Anadyr              Sun 2:19 AM
Anchorage        Sat 5:19 AM
Ankara             Sat 4:19 PM
Antananarivo    Sat 4:19 PM
Asuncion           Sat 9:19 AM
Athens              Sat 4:19 PM
Atlanta             Sat 9:19 AM
Auckland          Sun 1:19 AM


Group all commndlets by its PSSnapIn property

PS > get-command | where {$_.CommandType -eq "Cmdlet"} | group PSSnapIn | select count,name

Count  Name
-----    ----
  381   Microsoft.Exchange.Management.PowerShell.Admin
   47   Microsoft.PowerShell.Management
   12   Microsoft.PowerShell.Core
   58   Microsoft.PowerShell.Utility
   10   Microsoft.PowerShell.Security
    2   Microsoft.PowerShell.Host


Another way to list only Exchange 2007 command-lets

PS > get-command | where {$ -match "exchange"}

CommandType     Name
-----------             ----
Cmdlet                Add-ADPermission
Cmdlet                Add-AvailabilityAddressSpace
Cmdlet                Add-ContentFilterPhrase
Cmdlet                Add-DistributionGroupMember
Cmdlet                Add-ExchangeAdministrator
Cmdlet                Add-IPAllowListEntry
Cmdlet                Add-IPAllowListProvider
Cmdlet               Add-IPBlockListEntry
Cmdlet               Add-IPBlockListProvider
Cmdlet               Add-MailboxPermission


Or even better, filter with the pssnapin parameter 

PS > get-command -pssnapin Microsoft.Exchange.Management.PowerShell.Admin

CommandType     Name
-----------             ----
Cmdlet          Add-ADPermission
Cmdlet          Add-AvailabilityAddressSpace
Cmdlet          Add-ContentFilterPhrase
Cmdlet          Add-DistributionGroupMember
Cmdlet          Add-ExchangeAdministrator
Cmdlet          Add-IPAllowListEntry
Cmdlet          Add-IPAllowListProvider
Cmdlet          Add-IPBlockListEntry
Cmdlet          Add-IPBlockListProvider
Cmdlet          Add-MailboxPermission



Get all properties for a command-let

PS > (get-command select-string).parametersets[0].parameters | select name,ParameterType,aliases


Name               ParameterType                                                     Aliases
----                   -------------                                                          -------
InputObject       System.Management.Automation.PSObject              {}
Pattern             System.String[]                                                       {}
SimpleMatch     System.Management.Automation.SwitchParameter   {}
CaseSensitive   System.Management.Automation.SwitchParameter   {}
Quiet               System.Management.Automation.SwitchParameter   {}
List                  System.Management.Automation.SwitchParameter   {}
Include             System.String[]                                                       {}
Exclude            System.String[]                                                       {}
Verbose           System.Management.Automation.SwitchParameter   {vb}
Debug              System.Management.Automation.SwitchParameter  {db}
ErrorAction      System.Management.Automation.ActionPreference  {ea}
ErrorVariable    System.String                                                         {ev}
OutVariable      System.String                                                         {ov}
OutBuffer         System.Int32                                                          {ob}



I also wrote a function Get-Params which takes a cmdlet name and calculates the
shortest alias name for each cmdlet parameter. Very useful when typing commands interactively. It also emits some additional important columns.


>> Get-Params select-string


- select-string -

Name               Alias        Position     Type
----                  -----         --------       ----
caseSensitive   -c            named        SwitchParameter
exclude            -e            named        string[]
include             -inc         named        string[]
inputObject      -inp        named         psobject
list                   -l           named         SwitchParameter
path                 -path      2                string[]
pattern             -patt      1                string[]
quiet                -q          named        SwitchParameter
simpleMatch     -s          named        SwitchParameter
text                  -t          2                string[]

Thursday, September 13, 2007

In the search for aged files

There has been a lot of questions in the PowerShell news group regarding aged files. To simplify the search process I wrote the "Get-AgedFiles" function. It's simple to use and also pipeline friendly :-)

Get-AgedFiles searches for aged files based on this parameters:

-path, String, starting directory path ,wildcards supported (e.g., c:\scripts\*.ps1)

-age, Integer, file age , default value: 1.

-timePart, String,  can be one of this shortcuts values, default value "d":
    y     = Years
    m    = Months
    d     = Days
    h     = Hours
    mm = Minutes
    s     = seconds
    t     = Ticks

-property, String, The file property for dates comparison , default value "LastWriteTime":


-recurse, switch parameter, when declared, performs the search on sub directories.

Add support to get-childitem's include,exclude,filter parameters.




# Get all files from <D:\Backup>, 1 day old (only -path is required) based on <LastWriteTime> property
>> Get-AgedFiles -path "D:\Backup"

# Get all *.bkp files  from <D:\SQLBackup\*.bkp> 10 Days old based on <LastWriteTime> property
>> Get-AgedFiles -path "D:\SQLBackup\*.bkp" -age 10 -timePart d


# Get all files, recursively, from <c:\scripts> 6 Months old based on <CreationTime> property
>> Get-AgedFiles -path "c:\scripts" -age 6 -timePart m -property CreationTime -recurse


# Get all files from current directory, 7 Days old based on <LastWriteTime> property
# delete all
>> Get-AgedFiles . 7 d | remove-item -whatif


# Get all files ,recursively, from current directory, 1 Year old based on <LastWriteTime>
# property, move all to c:\scripts\temp
>> Get-AgedFiles . 1 y -recurse | move-item -destination c:\scripts\temp -whatif


The function file is also available for download here.



function Get-AgedFiles{
        [string]$path=$(throw "needs a path"),           

    switch ($timePart){
        "y"    {$timePart="TotalDays"; $age*=365 ; break}
        "m"    {$timePart="TotalDays"; $age*=30 ; break}
        "d"    {$timePart="TotalDays"; break}
        "h"    {$timePart="TotalHours"; break}
        "mm"  {$timePart="TotalMinutes"; break}
        "s"     {$timePart="TotalSeconds"; break}
        "t"     {$timePart="Ticks"; break}

        get-childitem $path -recurse | where {
           !$_.psiscontainer -and ((get-date).subtract($_.$property).$timePart -ge $age)
    } else {
        get-childitem $path | where {
           !$_.psiscontainer -and ((get-date).subtract($_.$property).$timePart -ge $age)



FTP transfers automation


Sapien technologies just released a com DLL to support simple FTP transfers. It supports the get/put FTP verbs, and I already tested them successfully. Its free to use and can be downloaded from Sapien's website. Thanks Sapien!

The download file includes two VBScripts files, one for upload and one for downloads. Here's a simple conversion to PowerShell.


Before starting using the functions, read the readme file, copy the DLL to system32 directory and register it:

regsvr32.exe ftp.dll


Cool, lets start




function Get-FTPFile{

    $ftp = new-object -com Primalscript.FTPTransfer;
    $ftp.Passive = 1;
    $ftp.port = $port;

    if($ftp.Connect($url,$user,$pass) -eq 0){
        "Can't connect. " + $ftp.Status;
    } else {
        "Starting download"
        If($ftp.Get($RemoteFilePath,$localPath) -eq 0){
            "Error downloading file " + $ftp.Status;
        } Else {
            "Download complete";



Get-FTPFile <ftpUrl> <port> <username> <password> <RemoteFilePath> <localDonloadPath>




function Put-FTPFile{

    $ftp = new-object -com Primalscript.FTPTransfer;
    $ftp.Passive = 1;
    $ftp.port = $port;

    if($ftp.Connect($url,$user,$pass) -eq 0){
        "Can't connect. " + $ftp.Status;
    } else {
        if($ftp.Put($RemotePath,$localFile) -eq 1){
            "success upload";           
        } else {
            "Error uploading file. " +$ftp.Status;



Get-FTPFile <ftpUrl> <port> <username> <password> <localUploadFile> <RemoteUploadPath>



I'm maybe missing something, but I noticed when using the Put-FTPFile that even if the upload was successful, Put() returns 1 (error???) instead of 0. I commented about it in Spain's website FTP tool page.


UPDATE 09/19/2007:

Here's Don Jones reply:

0 is bad, 1 is good news.

1 = success (C++ TRUE)
0 = failure (C++ FALSE)


After all, I did miss something :-)

Thanks Don.

Wednesday, September 12, 2007

/n software Bumper Stickers

A while ago I wrote on /n software free PowerShell stickers promotion.
Given the fact that remoting will be "in the heart" of PowerShell v2 and now that I have the sticker on my car, I wonder If I could start the car remotely from my office ? ;-)

New PowerShell video on dnrTV!

Scott Hanselman recorded another PowerShell introduction video on dnrTV!.
In the video you can see how to install and use MoW's super cool PowerTab and some other stuff like loading DLL and .NET classes along with basic PowerShell.

The video can be downloaded (~180MB).

Sunday, September 9, 2007

Converting PowerShell scripts


I released this scripts about a week ago, in respond to a discussion in the Windows PowerShell news group. The goal is to read a script content and replace all command-let names to there alias's and vice versa.

The two functions below takes a script file path as the argument and convert it respectively. 

The first function, "Convert-CmdletToAlias", is more *secure* to use as it search for all cmdlet names (which doesn't contain non special chars) and replaces each one it finds with the first corresponding alias (there can be more than one).

The second function, "Convert-AliasToCmdlet", is more *dangerous*. Richard Siddaway reported that there is one major draw back. If you used the "%" sign as an alias for the foreach-object cmdlet, things may go wrong and behave unexpectedly. The reason is that "%" is also the symbol for modulo.

Keith hill suggested to parse the script before the final conversion. Didn't find a way so far so any suggestions/modifications are welcomed.

#### EDIT (11/01/07)  #####
Check out Keith's post on parsing the script for errors prior to executing it
PowerShell QuickTip: Preparsing Scripts to Check for Syntax Errors

Anyway, the conversion is not written back to the script file and only displayed back to the console. If you decide to use the functions, make sure to create a backup copy of the original scripts and always test the final converted script before using it in production.




Convert-CmdletToAlias <filepath.ps1>
Convert-AliasToCmdlet <filepath.ps1>




function Convert-CmdletToAlias{
    if(!(test-path $filepath)){
        write-warning "Error: File doesnt exist";
    $src = [System.IO.File]::ReadAllText($filepath);
    $cmdlets = get-command | where {$_.CommandType -eq "Cmdlet"} | select name
    $cmdlets | foreach {

        $cmd = $;

        if($src -match "\b$cmd\b") {
            $alias = @(get-alias | where {$_.definition -eq $cmd});   
            $src=$src -replace($cmd,$alias[0]);




function Convert-AliasToCmdlet{

    if(!(test-path $filepath)) {
        write-warning "Error: File doesnt exist";
    $src = [System.IO.File]::ReadAllText($filepath);
    $aliases = get-alias | select Name,Definition

    $aliases | foreach {

        trap{write-host "ERROR cant process alias: <$alias> -f red -b black"; continue}
        $alias = @($_)[0];
        # for some reason this is not working "\b" +$regex +"\b"
        if($src -match "\s" +$regex +"\s"){
            $src=$src -replace($regex,$alias.definition);

Friday, September 7, 2007

Two PowerShell filters to aid with IP addresses

There are two major differences between functions and filters in PowerShell:

  1. The keyword, function vs filter.
  2. The available current variable inside the function/filter.

Both procedures can be used in pipelines.
When a function is used, the special variable $input is available to process through the input collection. For filters, the special variable $_ contains the current pipeline object.

Filter, as its name suggests will pass on only certain objects down the pipeline.
To demonstrate it, here are two filters to help where IP address are involved.


I have a text file were each line contains an IP address.

###### ip.txt #####

###### EOF #######


I want to get the content of this file and operate ONLY on live hosts.
The first filter will validate the IP address and stream down the pipe only valid ones.
The second filter will ping each valid host passed and test its availability,.
The third, ForEach cmdlet,  will run some code on the filtered IPs.


filter Validate-IPAddress {
    trap{write-host "$_ <$input>" -f red -b black; continue;}   


filter Ping-Host{
        # instantiate once for all piped objects
        $ping = new-object System.Net.NetworkInformation.Ping;
        # ping test
        if($stat -ne "Success"){write-warning "$_ is not available <$stat>";}
        else {$_;}


# cat = Get-Content
# -f = foreground
# -b = background

cat ip.txt | Validate-IPAddress | Ping-Host | foreach {    
    write-host "$_ is ALIVE, run some code" -f green -b black


# colored output , just for demonstrating is ALIVE, run some code
An invalid IP address was specified. <>
WARNING: is not available <TimedOut> is ALIVE, run some code
WARNING: is not available <TimedOut> is ALIVE, run some code



Here's the filters final version. You can see that only validated IPs and live hosts are processed.


filter Validate-IPAddress {

filter Ping-Host{
    begin{ $ping = new-object System.Net.NetworkInformation.Ping; }
    process{ if($ping.Send($_).status -eq "Success") {$_;} }   


cat ip.txt | Validate-IPAddress | Ping-Host | foreach {
    write-host "$_ is ALIVE, run some code" -f green -b black


# output is ALIVE, run some code is ALIVE, run some code is ALIVE, run some code

Thursday, September 6, 2007

Greetings from Windows PowerSell SDK

This is what happened when I first launched PowerShell after installing the SDK and .NET framework 3.

Does the installation tampers PowerShell core files? Why? What's wrong?

I restarted PowerShell and the error showed up again, Hmm.. I need to find out what went wrong, which cmdlet deals with execution restrictions...ahhhh...hmmm...right...
Get-ExecutionPolicy!  So I run it:

PS C:\Scripts> Get-ExecutionPolicy


It seems that the SDK setup process reverts PowerShell excecution policy back to its default "out-of-the-box" state - AllSigned, which means only scripts that were digitally signed can execute. 

PS C:\Scripts> Set-ExecutionPolicy RemoteSigned
PS C:\Scripts>

Restarted, and the message has gone.

Playing sounds in PowerShell


You can play sounds in PowerShell in different ways and integrate them in your scripts to provide some kind of indication. One way is with write-host. You can pass the escape sequence `a  to write host. `a is the Alert corresponding special character:

# play a screech sound (through the internal speaker, not useful on laptops)

write-host "`a";


Another way is with a .NET class (.wav format).

# play the file once
$sound = new-Object System.Media.SoundPlayer;

Note that the following process is asynchronously, meaning that the script wont hang until sound has finished playing. This example plays the file repeatedly until a condition met

$sound = new-Object System.Media.SoundPlayer;

1..10 | foreach {
    if($_ -gt 5){$flag=$true} else{sleep -s 1}
    if($flag) { $sound.Stop() }

write-host "Done";

There is another class called System.Media.SystemSounds. Pipe the class to get-member to reflect its static members:


PS C:\Scripts> [System.Media.SystemSounds] | gm -static

   TypeName: System.Media.SystemSounds

Name            MemberType  Definition
----               ----------         ----------
Asterisk        Property         static System.Media.SystemSound Asterisk {get;}
Beep             Property         static System.Media.SystemSound Beep {get;}
Exclamation  Property         static System.Media.SystemSound Exclamation {get;}
Hand            Property         static System.Media.SystemSound Hand {get;}
Question       Property         static System.Media.SystemSound Question {get;}


Each property emitted represents a system sound type. Now, Pipe each property to get-member


PS C:\Scripts> [System.Media.SystemSounds]::Asterisk | gm

   TypeName: System.Media.SystemSound

Name         MemberType   Definition
----            ----------         ----------
Play           Method           System.Void Play()
ToString    Method           System.String ToString()


Great, we can use the Play method,test each line in your console:



Finally, here is a one-liner for the job. It Utilizes the SoundPlayer(String) Constructor. It Initializes a new instance of the SoundPlayer class, and attaches the specified .wav file.

(new-object Media.SoundPlayer "C:\WINDOWS\Media\notify.wav").play();

.NET Remoting articles

Three articles on remoting published on The Code Project website.
As Jeffrey Snover stated on his last interview with  ('remoting' will be built into the heart of the PowerShell engine).
At the meantime here are some others techniques in .NET.

Just .NET Remoting

A simple .NET Remoting example in three parts: the interface, the server, and the client. The client asks for your name, then sends it to the server. The server returns a personal greeting.

Remote Administration Tool (RAT) in C# - Part 3 (Reverse Connection Shell)
How to access server behind a gateway firewall using reverse connection shell technique

Remote console that allows to send commands to remote computer located in the Internet.

All articles includes source file in C#

Bubble-sort in a batch file

Miszou has an interesting article (Advanced Batch File Techniques). Part 1 on implementation of a bubble-sort, using only Windows Batch files. Although this technique can be achieved easily in PowerShell (sort-object) it is worth mentioning.


Wednesday, September 5, 2007

Manage your SQL server with PowerShell and SMO

There is an excellent six part article walk through on Database Journal website by Muthusamy Anantha Kumar (aka The MAK) on PowerShell and SQL Server 2005 SMO. Everything you need to know in order to operate on SQL. The first parts introducing PowerShell concepts and some cmdlets to work with. From part III and on, there are fine examples on connecting to SQL server, collecting server information from different servers, create and backup databases and a lot more. A must read!

Check it out:

PowerShell and SQL Server 2005 SMO – Part I 
PowerShell and SQL Server 2005 SMO – Part II
PowerShell and SQL Server 2005 SMO – Part III
PowerShell and SQL Server 2005 SMO – Part IV
PowerShell and SQL Server 2005 SMO – Part V
PowerShell and SQL Server 2005 SMO – Part VI

Monday, September 3, 2007

First Hebrew PowerShell webcast

Thanks to Shahar Gvirtz for recording this 30 minutes webcast. Now the Israeli audience have a first glimpse (in Hebrew) to the power of the shell. I saw it and learned a few things. Cheers!.


Shahar explains the following topics:

  • What is PowerShell
  • How to use the Cmdlets
  • Demo of using .NET classses
  • Writing a cmdlet for IIS7 in Visaul Studio
  • Creating the installer for the cmdlet
  • Using the cmdlet.
  • How to persist the cmdlet using PowerShell console files

You can download the webcast from here.

Sunday, September 2, 2007

Can you hack into a non internet connected network?

Sounds impossible, right... WRONNNG.

I saw this on a hebrew security/hacking blog by Guy Mizrahi: Zull, A Hacker's diary. This is the starting paragraph of the original news:

"The first time Scott Lunsford offered to hack into a nuclear power station, he was told it would be impossible. There was no way, the plant's owners claimed, that their critical components could be accessed from the Internet. Lunsford, a researcher for IBM's Internet Security Systems, found otherwise."

I wonder what other things we dont know about? One day we all will vanish in a click of a key|mouse|button and wont even notice. Read the rest on Forbes website.

Windows PowerShell Cookbook: Rough Cuts Version

A new PowerShell book Windows PowerShell: The Definitive Guide Guide , by Lee Holmes and Microsoft PowerShell team developer, will publish next month.

Just by browsing the rough cuts version and the sample scripts I can imagine what the book contains (start to rub my hands). 

O'Reilly offers parts of the book for online reading (check the appendixes starting at Regular Expression Reference and downward).

Lets start cooking!

Saturday, September 1, 2007

How to validate DistinguishedName strings

By nature, Exchange 2007 is strongly integrated with Active Directory (AD) and as such, it exposes new types to interact with AD. A lot of the user object properties across all the AD forest are made available through the Get-User command-let. 
If you need to pass strings as "DistinguishedName" this function can help you validate them.


function Validate-DistinguishedName{


>> Validate-DistinguishedName "CN=Administrator,CN=Users,DC=homelab,DC=loc"

>> Validate-DistinguishedName "CN=Administrator,CN=Users,DC=homelab"

>> Validate-DistinguishedName "CN=Administrator,CN=Users"

>> Validate-DistinguishedName "Shay@homelab.loc"

>> Validate-DistinguishedName "AB=CD,EF= G H"

Colorize matching output in the pipeline


The other day I was looking for an option to colorize certain rows when using Format-* cmdlets. None of the cmdlets (including out-host, out-default) gives you the option to do that unless you define a custom PowerShell format/view file (ps1xml). I wish the Format-* cmdlets had a scriptblock parameter to which I can pass a piece of code that will colorize certain rows based on a criteria (maybe I'll fill a suggestion on Microsoft connect).

The major drawback of this custom format files is that you have to build them (requires some experience) for each type you want to print out, loading them into your environment via Update-FormatData and then executing them with the -view  parameter of the format-table or format-custom cmdlets.

This method involves too much configuration and I was looking for a way to make the process more portable, depend less and operatable on any given command-let,  so I can easily use it on any PowerShell machine or script.

Lets say you want to colorize all text file names that begins with "s".


PS C:\Scripts> Get-ChildItem $env:WINDIR *.txt

    Directory: Microsoft.PowerShell.Core\FileSystem::C:\WINDOWS

Mode       LastWriteTime               Length       Name
----          -------------                     ------         ----
-a---         7/28/2007 3:25 PM       8184          ModemLog_Agere Systems HDA Modem.txt
-a---         6/21/2007 11:43 AM     38280        ModemLog_HTC USB Modem.txt
-a---         3/5/2007   6:17 PM       3075         OEWABLog.txt
-a---         8/30/2007 10:51 AM     32622        SchedLgU.Txt
-a---         3/1/2007   1:56 AM       1012440    setuplog.txt
-a---         7/31/2006  7:41 PM       726072     SIGVERIF.TXT


Get-ChildItem $env:WINDIR *.txt | foreach {
    if($ -match "^s.*"){
        write-host $_ -ForegroundColor "green";
    } else {
        write-host $_ -ForegroundColor "gray"


ModemLog_Agere Systems HDA Modem.txt
ModemLog_HTC USB Modem.txt

As you can see, the foreach output contains only file names, no table headers nor additional property columns. It sure doesn't look like Format-Table's output.

For about an hour or so I was struggling with PowerShell commands and made a lot of experiments. Finally, I was able to construct a filter. The filter accepts three parameters.
Color, property name to match against  and a regular expression pattern.
Surprisingly, the solution was pretty simple. Without the filter the code looks like:

Get-ChildItem $env:WINDIR *.txt | foreach {
    if($ -match "^s.*"){
        [console]::ForegroundColor="green"; $_;
    } else {
        [console]::ForegroundColor="white"; $_;


Well, here's the code for the filter:


filter colorize-row{
        [string]$regex=$(throw "must supply regular expression pattern")

    # save current console colors

    if($_.$prop -match $regex){[console]::ForegroundColor=$color; $_}
    else{[console]::ForegroundColor=$fgc; $_}

    # revert to saved console colors


# get all text files in c:\windows that begins with "s", use default filter color 

PS C:\Scripts> Get-ChildItem $env:WINDIR *.txt | colorize-row -regex "^s.*"

    Directory: Microsoft.PowerShell.Core\FileSystem::C:\WINDOWS

Mode       LastWriteTime               Length       Name
----          -------------                     ------         ----
-a---         7/28/2007 3:25 PM       8184          ModemLog_Agere Systems HDA Modem.txt
-a---         6/21/2007 11:43 AM     38280        ModemLog_HTC USB Modem.txt
-a---         3/5/2007   6:17 PM       3075         OEWABLog.txt
-a---         8/30/2007 10:51 AM     32622        SchedLgU.Txt
-a---         3/1/2007   1:56 AM       1012440    setuplog.txt
-a---         7/31/2006  7:41 PM       726072     SIGVERIF.TXT


Now with get-process:

# get all processes which contains "svchost" in their names, colorize in yellow

PS C:\Scripts> get-process | colorize-row -regex "svchost" -prop "name" -color yellow

Handles  NPM(K)    PM(K)    WS(K) VM(M)   CPU(s)   Id      ProcessName
-------      ------     -----       -----    -----      ------     --      -----------
     68       3           816       1264    14        0.02      524     sqlbrowser
    311       9          34172    20632  1100    1.02      436     sqlservr
     84       2           1064     1616     20       0.44      548     sqlwriter
   1894     246        34804   38528   217     197.80   180     svchost
    104       5          1772     2336     33       10.56    332     svchost
    126       4          2616     2160     36       3.80      580     svchost
    284      12         8124     5084     47       2.45      1152    svchost
     75       3          2332     1468     31       0.06       1512   svchost
    230       6         3464     2776     65       72.11     1640   svchost
    428      14        2368     2852     40       15.72     1692   svchost
     95      10        1796     1460      37       0.28       2696   svchost

    807       0        0          244         2        359.94    4        System
    207       7        6608     9788      62       114.42   5828    TextPad




About PowerShell Console Colors

You can specify one of the following enumeration values to the filter -color parameter. To get all possible enumeration color names/values:

>> [consolecolor]::GetNames([System.ConsoleColor])
>> [consolecolor]::GetValues([System.ConsoleColor])

-or- better use the following to generate a table of color names and there corresponding numeric values:


0..($colors.length-1) | select @{n="Name";e={[string][enum]::parse([ConsoleColor],$_)}}, @{n="Constant";e={$_}} | format-table -autosize


Name             Constant
----                   --------
Black                        0
DarkBlue                   1
DarkGreen                2
DarkCyan                  3
DarkRed                   4
DarkMagenta            5
DarkYellow                6
Gray                         7
DarkGray                  8
Blue                          9
Green                      10
Cyan                        11
Red                         12
Magenta                  13
Yellow                     14
White                     15