PowerShell Cookbook

Search

Categories

 

On this page

PowerShell books becoming available
Determining your 401(k) Contributions
Break your Writer’s Block
Filtering on the Certificate Provider
Outlook Automation in PowerShell - Calendar Scrubbing
TechTalk Interview Posted
Getting Started with PowerShell
Add Custom Methods and Properties to Types in PowerShell
Scripting Network / TCP Connections in PowerShell
PowerShell Prompt Here PowerToy

Archive

Blogroll

Disclaimer
I work for Microsoft.

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

RSS 2.0 | Atom 1.0 | CDF

Send mail to the author(s) E-mail

Total Posts: 214
This Year: 14
This Month: 1
This Week: 0
Comments: 522

Sign In

 Friday, January 12, 2007
Friday, January 12, 2007 10:24:20 PM (Pacific Standard Time, UTC-08:00) ( )

Some more PowerShell books are becoming available -- this is a very exciting time!

First, there's Don Jones & Jeffrey Hicks' PowerShell: TFM
Second, there's Jerry Lee Ford Jr.'s Microsoft Windows Powershell Programming for the Absolute Beginner

Congrats on a huge milestone, and thank you for your contribution!

Comments [1] | | # 
 Thursday, January 11, 2007
Friday, January 12, 2007 2:42:30 AM (Pacific Standard Time, UTC-08:00) ( )

On January 1, 2006, many companies began to offer the Roth 401(k): an alternative to the traditional 401(k) that allows you to contribute your money in after-tax dollars, rather than pre-tax dollars. Most (in my opinion incorrectly) summarize the difference as the following choice: "If you think you might be in a higher income tax bracket when you withdraw the funds, invest in the Roth 401(k). Otherwise, invest in a traditional 401(k)." Omar Shahine points to a good summary here: http://www.shahine.com/omar/Roth401k.aspx. (This started as a comment on that article, but quickly grew to a post in of itself.)

Here are some examples, for somebody that is married, filing separately.

Current Income Post-Retirement
Income (estimated)
Current Tax Rate
(marginal)
Post-Retirement
Tax Rate (effective) *
Roth or
Traditional
Difference
per $1000
30,000

30,000

15% 13% Traditional 0
30,000

60,000

15% 19% Roth 40
30,000

90,000

15% 22% Roth 70
30,000

120,000

15% 25% Roth 100
30,000

200,000

15% 28% Roth 130
60,000

30,000

25% 13% Traditional 120
60,000

60,000

25% 19% Traditional 60
60,000

90,000

25% 22% Traditional 30
60,000

120,000

25% 25% Roth 0
60,000

200,000

25% 28% Roth 30
90,000

30,000**

28%

13% Traditional 150
90,000

60,000**

28% 19% Traditional 90
90,000

90,000

28% 22% Traditional 60
90,000

120,000

28% 25% Traditional 30
90,000

200,000

28% 28% Roth 0
120,000

30,000**

33% 13% Traditional 200
120,000

60,000**

33% 19% Traditional 140
120,000

90,000**

33% 22% Traditional 110
120,000

120,000

33% 25% Traditional 80
120,000

200,000

33% 28% Traditional 50
200,000

30,000**

35% 13% Traditional 220
200,000

60,000**

35% 19% Traditional 160
200,000

90,000**

35% 22% Traditional 130
200,000

120,000

35% 25% Traditional 100
200,000

200,000

35% 28% Traditional 70

 

* Assumes that tax rates will be the same as they are today.
Higher future tax rates would increasingly favour a Roth 401(k), while lower rates would favour a Traditional 401(k)
** Unlikely, given a medium-to-aggressive savings philosophy that would normally guarantee a higher post-retirement income
Effective tax rates generated by http://www.moneychimp.com/features/tax_brackets.htm

Notice the "Difference per $1000" column. That shows how many dollars (per $1000) it costs if you make a mistake. Worrying about making a mistake causes people a lot of heartburn, so let's drill into that a bit more. What's the worst mistake you can make? It's the row that shows you earning $200,000 now, contributing to a Roth 401(k), and then withdrawing $30,000 per year during retirement. Over your entire 30-year retirement, that would cost 30 years * $30,000 per year / 1,000 * 220 wasted per thousand = $198,000. That's about $6600 per year. Not good, but not the end of the world. Given a medium-to-aggressive savings strategy, this scenario is also highly unlikely.

How should you estimate your post-retirement income? It will be based on how much you've managed to save before retirement, which is a dependent on how much you can save per year. Assuming a 6% annual rate of return, this PowerShell calculation shows how to estimate your "nest egg" for retirement, assuming that you contribute $5,000 per year:

PS >$balance = 0
PS >$contribution = 5000
PS >1..30 | % { $balance += $contribution; $balance *= 1.06 }
PS >$balance
419008.386940671

Now, for your annual retirement income, you can play around with your withdraw rate on this $419,008 nest egg until the final balance nears zero:

PS >$balance = 419008; 1..30 | % { $balance -= 28700; $balance *= 1.06 }; $balance
1460.60834058789

So, contributing around $5,000 per year gives you a post-retirement income of about $29,000. That will remain $29,000 if the contributions were through a Roth 401(k), but will turn to about $25,000 if the contributions were through a traditional 401(k).

Here's one thing that may play a part in your 401(k) calculations: how much you can hope to retire with. Both the traditional and Roth 401(k) plans have annual contribution limits of $15,500 (expected to increase by $500 per year.) If you are in a position to save the maximum, your choice of 401 vehicle makes a significant difference on your post-retirement income if you base it on your 401(k) alone.

If you manage to save the maximum contribution per year, that leaves you with:

PS >$balance = 0
PS >$contribution = 15000
PS >1..30 | % { $balance += $contribution; $balance *= 1.06; $contribution += 500 }
PS >$balance
1690372.4723898

Which gives an annual retirement income of about $116,000:

PS >$balance = 1690372; 1..30 | % { $balance -= 115800; $balance *= 1.06 }; $balance
4402.41939379471

If those funds were contributed through a traditional 401(k), that will shrink to about $87,000 after taxes. If those funds were contributed through a Roth 401(k), they will stay ay $116,000. Given the tax advantage of the Traditional 401(k), though, it may make sense in that situation to maximize your contribution to the Traditional 401(k), and apply the money you would have invested in the Roth 401(k) to other investment vehicles.

The interest earnings on those other vehicles will be taxed at 10%, though, so the answer to this is highly dependent on your personal situation. Take a salary that seems to most highly favour the Traditional IRA, but where that person can afford to max out the Roth 401(k). That is at the 35% tax bracket:

## Scenario 1: Max out Traditional

   # A) First, max out Traditional
   $balance = 0
   $contribution = 15000
   1..30 | % { $balance += $contribution; $contribution += 500; $balance *= 1.06 }
   $balance
   1690372

   $balance = 1690372; 1..30 | % { $balance -= 115800; $balance *= 1.06 }; $balance
   = $115,800 per year minus tax

   # B) Then, apply your extra (vs. the Roth) to mutual funds
   $balance = 0
   $contributed = 0
   $marginalTaxRate = 1.35
   $limit = 15000
   $contribution = (($limit * $marginalTaxRate) - $limit) / $marginalTaxRate
   1..30 | % {
      $balance += $contribution; $contributed += $contribution; $balance *= 1.06;
      $limit += 500; $contribution = (($limit * $marginalTaxRate) - $limit) / $marginalTaxRate
   }
   $balance = ($balance - $contributed) * 0.9 + $contributed
   $balance
   411725.799113175

   $balance = 411725; 1..30 | % { $balance -= 36200; $balance *= 1.06 }; $balance
   = 28,200 per year (tax free)

   # C) Then figure out the combination of investments

   Total = 144,000 pre tax (A + B) = 25% effective tax rate
          = 115,050 per year after tax (0.75 * A + B)

## Scenario 2: Max out Roth

   $balance = 0
   $contribution = 15000
   1..30 | % { $balance += $contribution; $balance *= 1.06; $contribution += 500 }
   $balance
   1690372.4723898

   $balance = 1690372; 1..30 | % { $balance -= 115800; $balance *= 1.06 }; $balance
   = 115,800 per year after tax

Lower tax brackets than 35% (ie all of them) favour the Roth even more heavily if that person can afford to max out their contributions. As always, make your own informed decisions regarding investments, but hopefully this exercise can help clarify some things.

 

[Edit: Updated table -- Brian Kramp reminded me that Roth 401(k) contributions are taxed at your marginal tax rate, not the effective tax rate.]
[Edit2: Added calculations for investing in mutual funds]

Comments [0] | | # 
 Wednesday, January 10, 2007
Thursday, January 11, 2007 1:20:35 AM (Pacific Standard Time, UTC-08:00) ( )

In writing, overly-ambitious goals often mean not writing at all.

It sounds odd, but it’s true. Take most tumbleweed-ridden, ghost-town technical blogs. From my experience, the vast majority of them have authors that are working on this educational, pedagogical masterpiece -- but their Magnum Opus sits in a half-finished Word document on their hard drive somewhere.  All of the sudden, the joy of writing begins to leech away: they want to blog something, but this 2,000 word monster draft mocks them from the shadows. Instead, they put it off for another day when they have the energy to tackle this massive pile of words again. Internal resistance builds up, then the blog goes silent.

Writing doesn’t need to be this way. Writing is about expression, communication, and good old-fashioned fun. The world appreciates your input and insight, even if you contribute just a little.

You solve problems all day.  Even if it’s a relatively simple solution (script, house repair tip, way to get better gas mileage,) write about it anyways. Those looking to solve that same problem in the future will thank you.

One tip that’s helpful for getting into a state of writing flow is to get rid of “The Judge.” As you write, The Judge hampers you. He or she reminds you about spelling, grammar, or that you aren’t using words that sound smart enough. If The Judge feels particularly mean, he or she might comment on a much more personal level – maybe that your writing is boring, stupid, or a down-right waste of words.

It’s easy to tell when The Judge is around. You write slowly, re-thinking nearly every word you write. When he or she is especially ornery, you even second-guess characters in a word. When you finally squeeze out a sentence, you re-visit the sentence to add complexity, redundant words, and other language thickening  goop.

An excellent way to get rid of The Judge is to make him or her sputter in fury and give up. Open up a new Word document, and write as much as you can for one minute. Write whatever comes to mind – about your day, you current piece, or even The Judge. Mash the keyboard and get a typo? Who cares – keep writing. Don’t have anything to say? Then write, “I don’t have anything to say right now.” No matter what, just keep writing. Your goal here is volume. Output. The maximum word count possible. If you can touch type, I’ve found that closing my eyes helps a lot. If you can’t touch type, don’t even bother looking at the screen.

At the end of the minute, ditch the Word document and get back to writing the piece you wanted to. As you get more practice with the exercise, you’ll find that you can produce more random junk in a minute than you could have imagined.

And what’s most important -- at the end of the exercise, you’ll find The Judge a lot less of an inhibition than before. So ditch your writer's block, and enjoy writing again.

Comments [1] | | # 
 Monday, January 08, 2007
Tuesday, January 09, 2007 5:29:22 AM (Pacific Standard Time, UTC-08:00) ( )

Well, after a long holiday break, the first thing on both of our minds, naturally, is filtering on the Certificate Provider.

A suggestion came up recently in an internal discussion list to add more dynamic parameters to Get-ChildItem when you're in the certificate provider. For example, the –CodeSign parameter is extremely useful:

[cert:\]
PS:2 > dir -rec -codesign


    Directory: Microsoft.PowerShell.Security\Certificate::CurrentUser\My


Thumbprint                                Subject
----------                                -------
5D103CCDCFE0D96748A305DFACA9C942ABFD73E7  CN=PowerShell User

 

[cert:\]
PS:3 > $cert = dir -Recurse -CodeSign

[cert:\]
PS:4 > Set-AuthenticodeSignature c:\temp\MyScript.ps1 $cert


    Directory: C:\temp


SignerCertificate                         Status                                 Path
-----------------                         ------                                 ----
5D103CCDCFE0D96748A305DFACA9C942ABFD73E7  Valid                                  MyScript.ps1

But – what about other certificate types, such as "Encrypting File System" ? We don’t expose this as a dynamic parameter, although that would be a useful feature.

However, since the certificate provider returns certificates in all of the object-oriented glory, you can use the Get-Member cmdlet to explore what’s available to you. In this case, the "Intended purpose" of the certificate comes from a certificate extension. One of those certificate extensions is the "Enhanced Key Usage" (EKU) – which contains a list of identifiers such as "Client Authentication," "Code Signing," etc.

Here is a function that gets a certificate by its EKU:

## Get-CertificateByEku.ps1
param($ekuName = $(throw "Please specify the friendly name of an Enhanced Key Usage (such as 'Code Signing'"))

foreach($cert in Get-ChildItem cert:\CurrentUser\My) {
   foreach($extension in $cert.Extensions)
   {
       foreach($certEku in $extension.EnhancedKeyUsages)
       {
           if($certEku.FriendlyName -eq $ekuName)
           {
               $cert
           }
       }
   }
}

And some output:

[C:\Temp] PS:90 > C:\temp\Get-CertificateByEku.ps1 "Encrypting File System"


    Directory: Microsoft.PowerShell.Security\Certificate::CurrentUser\My


Thumbprint                                Subject
----------                                -------
22D5904D20754371582F72FA158625FEB85F1345  CN=Lee Holmes


If you do this a lot, you might think about adding this as a property to the certificate itself: http://www.leeholmes.com/blog/AddCustomMethodsAndPropertiesToTypesInPowerShell.aspx.

Comments [0] | | # 
 Wednesday, December 13, 2006
Thursday, December 14, 2006 7:45:39 AM (Pacific Standard Time, UTC-08:00) ( )

Here's one thing you might have noticed happening to your Outlook calendar. As time goes on, the calendar titles get obscured by the ugly meeting barnacles such as "FW: " and "Updated: ". I don't know anybody that cares about that meta-data, but it lives on -- and I'd like to remove it.

Omar Shahine mentioned that he wrote an Outlook addin to do this once. Since I've been doing some mail management tasks with PowerShell lately (more on that in future posts,) I thought it might be a useful thing to demonstrate via Outlook's Outlook.Application COM object.

The script below is quite simple. Run "Set-CalendarTitles," watch the pretty progress bar, and enjoy your newly clean calendar.

##############################################################################
## Set-CalendarTitles.ps1
##
## Clean calendar titles to remove:
## "FW: ", "Updated: "
##############################################################################

function main
{
    ## Create the outlook application object, and connect to the calendar
    ## folder
    $olApp = new -com Outlook.Application
    $namespace = $olApp.GetNamespace("MAPI")
    $fldCalendar = $namespace.GetDefaultFolder(9)

    "Gathering calendar items"
    $items = $fldCalendar.Items

    ## Visit each item, updating progress as we go
    $counter = 0
    foreach($item in $items)
    {
        $status = "Processing item {0} of {1}: {2}" -f $counter,$items.Count,$item.Subject
        Write-Progress "Processing calendar items" $status -PercentComplete ($counter / $items.Count * 100)

        ## Remove the extra text
        cleanItem $item "FW: "
        cleanItem $item "Updated: "

        $counter++
    }
}

## Clean the title of a calendar entry if it matches
## searchString
function cleanItem($item, $searchString)
{
    if($item.Subject -match $searchString)
    {
        $item.Subject = $item.Subject -replace $searchString,""
        $item.Save()
    }
}

. main
Comments [5] | | # 
 Sunday, December 10, 2006
Monday, December 11, 2006 3:36:34 AM (Pacific Standard Time, UTC-08:00) ( )

If you missed the TechTalk radio interview (but wanted to hear it,) I've uploaded it here: http://www.leeholmes.com/projects/techtalk/techtalk_12-10-2006.mp3. Thanks, Tom, for an interesting interview!

 

Comments [0] | | # 
 Thursday, December 07, 2006
Friday, December 08, 2006 12:31:30 AM (Pacific Standard Time, UTC-08:00) ( )
One of the more common questions we get from people interested in PowerShell is "how do I get started?" They see  examples of PowerShell weilding its considerable strength, and are often intimidated by a perceived learning curve. It doesn't have to be that way, though — PowerShell is easy to start playing with, and easy to continue learning.

First of all, you’ll want to download PowerShell. If you visit http://www.microsoft.com/PowerShell, you’ll get to the PowerShell homepage that lists an enormous number of resources. One of the first resources is a link to the download location.

Next, just start the shell and start exploring. The DOS commands you may (or may not) be used to still work: dir, cd, ipconfig, etc. Many of the UNIX commands you may (or may not) be used to  still work: ls, cd, ps, etc.

Then, take a dip into the awesome documentation that PowerShell ships with. The PowerShell menu on your start menu links to them directly:

  • Getting started: A 32-page overview of PowerShell and its core concepts
  • Quick reference: A 2-page summary of PowerShell’s scripting language
  • User Guide: A 116-page user guide for PowerShell. Perhaps unbelievable but true, it is a really useful book that we include for free with the product.
  • Help content: A large amount of help is available through the Get-Help cmdlet  -- see both the Getting Started documentation and User Guide for more information about this powerful command.

However, some people just like to sit back and have their learning delivered to them. You’re in good company there, too, as there are plenty of those resources.

Ars Technica wrote probably the best online overview, back when PowerShell was called "Monad": http://arstechnica.com/guides/other/msh.ars/

There are also interviews, videos, screencasts, and more:

Once you start exploring deeper, there is a fantastic opportunity for continual learning:

And, did I mention that this hasn’t cost you a thing yet?

If you want books or individualized training, you continue to have many options:

  • O’Reilly’s PowerShell Cookbook: http://www.leeholmes.com/blog/PowerShellCookbookNowAvailable.aspx
    A 584 pages of PowerShell recipes that focus squarely on showing you how to use PowerShell to get your job done. It builds on a huge base of distilled knowledge, and includes:
    • Solutions to the most popular and searched-for TechNet / Script Center topics
    • Scripts that address the most common community, newsgroup, and new user questions
    • Scripts that wrap around and hide the complexity of advanced (but very useful) PowerShell scripting techniques
    • Task-based introduction to all of PowerShell’s major features
  • Bruce Payette's PowerShell in Action: http://www.amazon.com/Windows-Powershell-Action-Bruce-Payette/dp/1932394907
    The best in-depth book for the scripting language you could ask for, from one of its co-designers.
  • O’Reilly’s PowerShell Quick Reference: http://www.leeholmes.com/blog/OReillyPowerShellQuickReferenceNowAvailable.aspx
    My 120-page guide (in PDF format) that provides the essential reference material for your day-to-day use of PowerShell.  With a concise explanation at your fingertips, there is no need to memorize esoteric topics like regular expressions and string formatting specifiers. Aside from its straight factual reference material, the Quick Reference also provides an enormous amount of value by distilling large bodies of practical knowledge into their most usable forms, including: the most useful classes from the .NET Framework, useful WMI classes, and useful COM automation objects.
  • More books: http://www.amazon.com/s/?url=search-alias%3Dstripbooks&field-keywords=PowerShell
  • Internet serach for “PowerShell  training”

So, jump in and enjoy the revolution!

Comments [4] | | # 
 Monday, December 04, 2006
Monday, December 04, 2006 8:29:19 AM (Pacific Standard Time, UTC-08:00) ( )

To give a glimpse into the writing process behind my upcoming "Windows PowerShell - The Definitive Guide" (O'Reilly,) I'll occasionally post entries "as the author sees it."  This entry discusses PowerShell's type extension files.

 

Add Custom Methods and Properties to Types

Problem

You want to add your own custom properties or methods to all objects of a certain type.¶

Solution

Use custom type extension files to add custom members to all objects of a type.

Discussion

Although the Add-Member cmdlet is extremely useful in helping you add custom members to individual objects, it requires that you add the members to each object that you want to interact with. It does not allow you to automatically add them to all objects of that type. For that purpose, PowerShell supports another mechanism custom type extension files.

Type extensions are simple XML files that PowerShell interprets. They allow you (as the administrator of the system) to easily add your own features to any type exposed by the system. If you write code (i.e.: a script or function) that primarily interacts with a single type of object, then that code might be better suited as an extension to the type, instead.

Since type extension files are XML files, ensure that your customizations properly encode the characters that have special meaning in XML files such as <, >, and &

For example, imagine a script that returns the free disk space on a given drive. That might be helpful as a script, but you might find it easier to instead make PowerShell's PsDrive objects themselves tell you how much free space they have left.¶

Getting Started

If you haven't already, the first step in creating a types extension file is to create an empty one. The best location for this is probably in the same directory as your custom profile, with the name Types.Custom.ps1xml

Example 3-2. Sample Types.Custom.ps1xml file

<?xml version="1.0" encoding="utf-8" ?>¶

<Types>¶

</Types>¶

Next, add a few lines to your PowerShell profile so that PowerShell loads your type extensions during startup:¶

$typeFile = (join-path (split-path $profile) "Types.Custom.ps1xml")¶

Update-TypeData -PrependPath $typeFile¶

By default, PowerShell loads several type extensions from the Types.ps1xml file in PowerShell's installation directory. The Update-TypeData cmdlet tells PowerShell to also look in your Types.Custom.ps1xml file for extensions. The –PrependPath parameter makes PowerShell favour your extensions over the built-in ones should there be a conflict.¶

Once you have a custom types file to work with, adding functionality becomes relatively straight forward. As a theme, we will do exactly what we alluded to earlier: add functionality to PowerShell's PsDrive type.¶

To support this, we need to extend your custom types file so that it defines additions to the System.Management.Automation.PSDriveInfo type. This is the type that the Get-PsDrive cmdlet generates.¶

<?xml version="1.0" encoding="utf-8" ?>¶

<Types>¶

  <Type>

    <Name>System.Management.Automation.PSDriveInfo</Name>

    <Members>

          add members such as <ScriptProperty> here

    </Members>

  </Type>

</Types>¶

Add a ScriptProperty

A ScriptProperty allows you to add properties (that get and set information) to types, using PowerShell script as the extension language. It consists of three child elements: the Name of the property, the Getter of the property (via the GetScriptBlock child,) and the Setter of the property (via the SetScriptBlock child.)¶

In both the GetScriptBlock and SetScriptBlock sections, the $this variable refers to the current object being extended. In the SetScriptBlock section, the $args[0] variable represents the value that the user supplied as the right-hand side of the assignment.¶

The following example adds an AvailableFreeSpace ScriptProperty to PSDriveInfo. When you access the property, it returns