Archives for the ‘Uncategorized’ Category

Developing for an International Audience

One aspect of software development (scripting included) that burns every single developer is developing for an international audience. The most common cause of problems come from incorrect handling of dates and numbers, but there are many more.

I first cut my teeth on the topic when working on Encarta Online, via bugs teaching me the hard way! After that, I took some time to read the excellent documentation on MSDN that describes many of the intricacies: Developing World-Ready Applications, and especially Best Practices for Developing World-Ready Applications.

Of course, these principles don’t just apply to C# software developers. They apply equally well to PowerShell scripters! While PowerShell handles most of the complexity for you, there are some important things to be aware of. “Write Culture-Aware Scripts” in Chapter 13 of the PowerShell Cookbook describes the issues around writing world-ready PowerShell scripts.

If you REALLY want to understand some of the crazy issues in international software development, Michael Kaplan has a great MSDN blog to follow: Sorting it all Out.

Now, once you think you’ve got it all down, consider this harmless form that anybody might add to an application:

First Name: ___________________
Last Name: ___________________

If you’re writing this for an international audience, you’re going to alienate and confuse a huge portion of your audience. This W3C Internationalization document describes the problem with amazing clarity: Personal names around the world. As Reddit so clearly puts it, “If you’re asking for a first and last name, you’re doing it wrong!”

Enforcing Single-User Access to PowerShell Remoting

I got a question today with an interesting request – “How do I ensure that only one user can access a machine via PowerShell Remoting at a time?”

We do have direct functionality to limit multiple sessions, but it that limit applies to sessions by the same user – see: MaxShellsPerUser for full detail about that setting. If you are doing some sort of self-service portal where everybody shares the same local username / password, MaxShellsPerUser is one way to solve the problem.

More commonly, though, you might want to enforce this restriction across all users on the machine. One useful thing to configure then is MaxConcurrentUsers. Set MaxConcurrentUsers = 1 and MaxShellsPerUser = 1, and you’ve enforced single-user access.

If your needs are more complex than this, we can solve this with a custom PowerShell session configuration. Perhaps we’d like to offer unrestricted access to administrators on the (default) Microsoft.PowerShell configuration, but enforce throttling on a new one called “Contoso.Build”.

If you’ve never set up a custom session configuration before, there are many great resources. Ravikanth Chaganti, one of our awesome MVPs, has written a great introduction to them here:

This is also covered in the PowerShell Cookbook, recipe 29.20.

Now, once you’ve got a startup script for your custom session configuration, you can use a system-wide Named Mutex to enforce your quota limits. This is pretty advanced scripting, so don’t be worried if it looks intimidating.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025

function IsMachineIdle
{
   
$sessionHandle = New-Object System.Threading.Mutex $false,"PowerShellSessionMutex"
    $isAvailable = $false

    try
    {
       
$isAvailable = $sessionHandle.WaitOne(1000
)
    }
   
catch [System.Threading.AbandonedMutexException]
    {
       
$isAvailable = $true
    }

    $isAvailable
}

if(-not IsMachineIdle)
{
   
"Sorry, the machine is in use. Try again in 5 minutes."
    exit
}

## Continue on with preparing a session

If you want to get more advanced (saying who is logged on, etc.), roll up your sleeves and dig in!

Organizational Awareness with PowerShell

One of the things that’s always unsettled me a bit in a big organization is keeping yourself informed about changes that you care about, even when those changes never end up making it through the grape vine. For example:

  • Promotions. Of course you want to congratulate them!
  • Transfers. Finding out that a team has moved to another manager, but no “re-org” mail to let you know.
  • Leaving the company. Finding out that a co-worker has left the company or division.

Naturally, PowerShell is here to save the day! I’ve been running a script I call “OrgDiff” as a scheduled task now for a long time – it came about organically, and I look forward to its results every Monday.

Here a couple of great examples of a few little bits of code (75 lines) coming together to make something of surprising utility.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
param($User = ‘mybigboss’, $Limit = [Int32]::MaxValue, [Switch] $IncludeManager)

$depth = 0

## A list of all the domains that you want to search for
## a given user’s alias. Try to organize them in order
## of popularity so that your script runs as quick as possible.

$domains = "LDAP://DC=domain1,DC=corp,DC=contoso,DC=com",
    "LDAP://DC=vendors,DC=corp,DC=contoso,DC=com",
    "LDAP://DC=international,DC=corp,DC=contoso,DC=com"
   
## Find all of the direct reports for a given user
function GetReports($username, $depth
)
{
   
if(-not $username) { return
 }
   
if($depth -gt $Limit) { return
 }

    ## Go through all of the domains and try to find the user
    ## account in that domain
    foreach($domain in $domains
)
    {
       
$adsi = [ADSI] $domain

        $searcher = New-Object System.DirectoryServices.DirectorySearcher $adsi
        $searcher.Filter = "(&(objectClass=User)(mailnickname=$username))"
        $user = $searcher.
FindOne()
       
       
if($user) { break
 }
    }
   
   
if(-not $user
)
    {
       
Write-Error "Could not find $username"
        return
    }

    ## If we want the report to include information about the user’s manager,
    ## prepare that portion of the output.
    if($IncludeManager
)
    {
       
$manager = " – " +
            (($user.Properties.manager -split ‘,’)[0] -replace ‘CN=’,
)
    }
   
   
## Display the user’s name, alias, title, and (optionally) manager -
    ## indented according to how deep we’re investigating
    (" " * ($depth * 2)) + $user.Properties.name +
        " (" + $user.Properties.mailnickname +
        ") – $($user.Properties.title)$manager"
   
   
$depth++

    ## If the user has direct reports, call this function again to show
    ## their portion of the organization.
    foreach($directReport in @($user.Properties.
directreports))
    {
        
$report = [ADSI] "GC://$directReport"
         GetReports ($report.mailNickname) $depth
    }
}

GetReports $user $depth

Normal output looks like this:

Some Body (somebody) – GENERAL MANAGER
  Jeffrey Snover (jsnover) – DISTINGUISHED ENGINEER
  Cool Person (coolbeans) – BUSINESS ADMINISTRATOR
  Super Developer (superdev) – DIRECTOR OF DEVELOPMENT
    Dev Manager (devmgr) – DEVELOPMENT MANAGER
      Dev Lead (devlead) _ DEVELOPMENT LEAD
    Dev Manager2 (devmgr2) – DEVELOPMENT MANAGER
(etc)

Now, if you run a weekly script to generate this report into a text file on your hard drive, you can use PowerShell to compare last week’s file with this week’s file. However, when we just compare files line-by-line, you’ll lose the indentation – and therefore the context of who their manager is. That leads to the need for the “-IncludeManager” switch, which generates output more like this:

Some Body (somebody) – GENERAL MANAGER
  Jeffrey Snover (jsnover) – DISTINGUISHED ENGINEER – Some Body
  Cool Person (coolbeans) – BUSINESS ADMINISTRATOR – Some Body
  Super Developer (superdev) – DIRECTOR OF DEVELOPMENT – Some Body
    Dev Manager (devmgr) – DEVELOPMENT MANAGER – Super Developer
      Dev Lead (devlead) _ DEVELOPMENT LEAD – Dev Manager
    Dev Manager2 (devmgr2) – DEVELOPMENT MANAGER – Dev Manager
(etc)

Here’s “Compare-OrgChart.ps1”, a script that starts to show useful differences between two files:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
param($BeforePath, $AfterPath)

## Get the content from the first file and the second file, then sort them by name.
## This lets Compare-Object focus primarily on changes to people, and not be
## impacted by random orderings that might come back from Active Directory

$chart1 = Get-Content $BeforePath | % { $_.Trim() } | Sort-Object
$chart2 = Get-Content $AfterPath | % { $_.Trim() } | Sort-Object

## The format of the lines are:
## Super Developer (superdev) – DIRECTOR OF DEVELOPMENT
## So we tell Compare-Object to look for any changes, sort by only their name,
## and after that sort by "Before" then "After"

Compare-Object $chart1 $chart2 |
    Sort { ($_.InputObject -split ‘-’)[0] },SideIndicator

Now, pretend the following happens:

  • Jeffrey gets promoted to Technical Fellow (congrats!)
  • “Dev Manager” leaves the group
  • “Dev Lead” moves under “Dev Manager2”

Here’s the org chart:

Some Body (somebody) – GENERAL MANAGER
  Jeffrey Snover (jsnover) – TECHNICAL FELLOW – Some Body
  Cool Person (coolbeans) – BUSINESS ADMINISTRATOR – Some Body
  Super Developer (superdev) – DIRECTOR OF DEVELOPMENT – Some Body
    Dev Manager2 (devmgr2) – DEVELOPMENT MANAGER – Dev Manager
      Dev Lead (devlead) _ DEVELOPMENT LEAD – Dev Manager2

(etc)

From there, we get this output:

PS C:\Users\Lee> Compare-OrgChart C:\temp\org_before.txt C:\temp\org_after.txt

InputObject                                                   SideIndicator
———–                                                   ————-
Dev Lead (devlead) _ DEVELOPMENT LEAD – Dev Manager           <=
Dev Lead (devlead) _ DEVELOPMENT LEAD – Dev Manager2          =>
Dev Manager (devmgr) – DEVELOPMENT MANAGER – Super Developer  <=
Jeffrey Snover (jsnover) – DISTINGUISHED ENGINEER – Some Body <=
Jeffrey Snover (jsnover) – TECHNICAL FELLOW – Some Body       =>

That’s pretty cool! Now all we need is a little way to stitch it all together. Here’s a very simple “Update-OrgDiff.ps1” script. All it does is fetch the newest org chart, run the comparison, and email me the results.

001
002
003
004
005
006
007
008
009
010
$mailRecipient = "me@example.com"

$file = "OrgChart-{0}-{1}-{2}.txt" -f (Get-Date).Month,(Get-Date).Day,(Get-Date).Year
Get-OrgChart somebody -IncludeManager > "c:\temp\$file"
$results =  dir c:\temp\OrgChart-* | Sort LastWriteTime | Select -Last 2
$report = Compare-OrgChart $results[0].FullName $results[1].FullName |
    Format-Table -Auto | Out-String -Width 100

Send-MailMessage -To $mailRecipient -From $mailRecipient -Subject OrgDiff `
   
-BodyAsHtml "<html><body><pre>$report</pre></body></html>" -SmtpServer smtphost

Set this up as a scheduled task, and you’re golden!

WMRRA Taste of Racing

Last week, a co-worker mentioned a event being offered again by WMRRA (Washington Motorcycle Road Racing Association) – the “Taste of Racing”.

227781_1684774685881_1433050347_31322912_7171556_n[1]

For $20, you get to bring your bike on the track for 20 minutes. A handful of control riders take you through some familiarity laps (and keep things under control during the ride.)

222216_1684774925887_1433050347_31322913_1833109_n[1]

But then it’s just you and wide-open road.

You know those days when you get an on-ramp all to yourself and smile? Imagine 2 twisty miles of that, designed and shared by people who love it just as much as you do.

This was the "Bus Stop", a set of chicanes to slow things down before the main straightaway.

The straightaway leads to a beautiful sweeping left that you get to take with great speed, and finally get to enjoy all of the rubber in your tires.

From my GPS – a great mix of slow technical twisties, combined with long stretches of wide-open throttle :)

Morser – Keyboard Morse Input Device

[Edit: Please see V2 here: http://www.leeholmes.com/blog/2011/10/04/morser-keyboard-morse-input-device-2/]

PowerShell and HTML5 Prototype

Every once in a while, we look to the PowerShell community for feedback on possible design directions on PowerShell. We’ve been exploring the integration of PowerShell and HTML5, and would love to get your feedback on a proof-of-concept.

This prototype uses the System.Net.WebClient class to “bootstrap” itself, and from there, awesomenes ensues.

From a PowerShell prompt, run the following command:

iex (New-Object Net.WebClient).DownloadString(“http://bit.ly/e0Mw9w”)

If you like this, we’ve crowd-sourced some other PowerShell investigations in the past, as well:

An Introduction to Hashing and Hash Codes

For the next couple of posts, we’ll be talking about an important facet of computer security – hashing, hash codes, and especially what makes “cryptographically strong” hashes special.

If you’re a developer or scripter and things stop making sense – let me know. It’s my fault, not yours.

One common problem in software is the desire to determine if two things (i.e.: files, documents, passwords) are the same or different without having to compare those things verbatim. For example, after downloading a huge file – how can you find out if it was corrupted during the transfer? It turns out that asking the web server, “Are you sure?” for every byte doesn’t really solve the problem.

One solution to this issue is called hashing: creating a small unique identifier for an item based on its properties or features. We use this concept all the time in real life: when describing people by their gender, height, race, and hair colour, or when quickly deciding if two files are the same by checking their name and size. This unique identifier is called a hash or hash code.

One thing the definition mentions is “based on its properties or features”. There are plenty of small unique identifiers that don’t qualify as hash codes: phone numbers, web addresses, and social security numbers, just to name a few. These are not derived from the thing they describe: you can’t look at somebody and decide that their social security number is 078-05-1120. Being able to independently create a hash code based on the inherent features of the item is crucial to its success. Along with being feature-based, the hash code for an item must be predictable. If two people calculate the hash code for the same item, they must arrive at the same answer.

Given these properties, we now have a good way to verify things like large file transfers. For example, many download links (such as the Visual Studio 2010 Trial) also document the hash code of the files:

(…)
The CRC and SHA1 hash values of your generated ISO image should match these:

  • CRC: 0x280d8d93
  • SHA-1: 0x7790db7d2aac9e1ee8baa34d42988577689c9e7a

The terms CRC and SHA-1 are the names of two common ways to calculate the hash of a file. An other common way (aka: “Hash Algorithm”) is called MD5. In MD5, the hash code is often called the “MD5 sum.”

After you’ve downloaded the file, you run a tool to calculate the hash code of the file and verify that it matches. A PowerShell script to do that is included in the PowerShell Cookbook (and on PoshCode.org): http://poshcode.org/2154.

More Pigeons than Pigeonholes!

One thing we had mentioned as a feature of good hash algorithms is that they return different hash codes for different items. But we also said that the hash codes must be shorter than comparing every bit of the item itself. A typical DVD download is about 5,000,000,000 bytes. A typical hash code is 256 bytes. This is a problem!

Think about being asked to take the numbers 1 to 100 and represent them with a hash code that’s only one digit. Intuitively, you know it is impossible to identify 100 different numbers uniquely with only 10 numbers. This is known in math and computer circles as the “pigeonhole principle” – if you have to fit 11 pigeons into 10 holes, at least one hole must have two or more pigeons. When you apply that to hash algorithms (representing a 5,000,000,000 byte file with 256 bytes), some of these files are going to have to share the same hash code. This is called a hash collision.

If you’ve only got 256 bytes to represent all possible files, minimizing these hash collisions is crucial. If our pretend 1-to-100 hash algorithm that uses only the digits 0-9 tends to cluster around the number ‘3’, you’re going to have more collisions than necessary. While this might seem obvious when you consider the entire range of possibilities, it should also apply to smaller portions of the range. If our hash algorithm for the numbers 1-100 just used the first digit, we’d have nothing but collisions if you only checked the numbers 10-19. Or only checked the numbers 20-29.

To make a hash algorithm as effective as possible, designers try to make the algorithm distribute its answers as close to randomly as possible. Not truly random, mind you. The hash code for an item must still be the same every time you figure it out: that is non-negotiable.

Here’s an example of a bad hash code: ours. Notice the clustering of collisions:

image

Take a look at a better one:

image

But Some People are Eeeevil

Using hash codes for simple content verification is one thing. But what if an attacker is trying to trick you with hash code collisions? For example, can you use that Visual Studio download if it’s from your worst enemy – but the hash code still matches? It depends on the algorithm! We’ll talk about the security implications of hash algorithms next.

A very touching final goodbye

The PowerShell community lost a great member last night. Carrying out one of his final wishes, his family posted this on Twitter last night:

We are sad to tell you, Andy "the most passionate Powersheller" passed away on 12th Jan 2011, and wanted us to say goodbye to you all. Andy
http://twitter.com/AndyTearle/status/25414783826141184

Rest in peace, Andy.

Setting up a Small Business / Personal Website

I recently had a friend ask me how to set up a website. They wanted more than a blog – something to post their artwork on, put up a gallery, and perhaps sell some things.

There are a couple of options – it just depends on how ambitious you are!

At the most basic level, here are some free options:

There’s also Etsy – something like http://www.etsy.com/people/paintedcottages?ref=ls_profile. Etsy isn’t free  – it’s about 7 cents per item per month that you have listed, plus 3% of an item when it sells. However, it does expose you to more potential customers, although I have no personal experience.

Now, if you don’t want the URL to say “wordpress.com” or “sites.google.com” or “officelive.com”, you can buy a domain name. They’re about $10 per year. Despite the internet domain name gold rush having long since passed, there are plenty of good ones still available. Once you’ve done that, you can set up your website (Google / WordPress / Microsoft) to work with it.

For the Microsoft one, it’s free. For the WordPress option, it’s about $12 to $17 per year: http://en.wordpress.com/products/.  For the Google option, this costs about $50 per year, but comes with a lot of other stuff: http://www.google.com/apps/intl/en/business/features.html.

The major benefit of these three sites is that they are easy to get started with. To make that possible, though, they put a lot of limits on what you can do. For example, you won’t be able to add shopping carts or more advanced features like that.

If you need / want more advanced features, the next step is to go with a company that just gives you web space. This is called “Web hosting”. That costs about $60 per year, and you can do anything you want there. You don’t have to worry about this right away, though, since it is easy to move to a web host from any of these starter sites.

The big work is just going to be designing your site to look the way you want. Fortunately, starting with a template (like you do with those starter sites) gets you a bunch of the way there. After that – it’s tweaking colours, uploading images and whatever other kind of personalization you want to do. When it comes to getting high-quality images easily, you’re in luck. There are a lot of images on Flickr that are tagged with licenses that let you use them for commercial work. For example: http://www.flickr.com/search/?l=comm&mt=all&adv=1&w=all&q=furniture&m=text.

So here’s an open question – know of any other good starter sites that are both easy and powerful?

Feedback is Your Greatest Gift

When is the last time you gave the gift of appreciation? I don’t mean “Thank You” as the social lubricant. I mean a thoughtful expression of exactly what somebody has done, and the impact it had on you.

One of my favourite work habits is expressing this simple act of ‘Thank You’. Not always to the person themselves – but to their manager.

In every company I’ve ever seen, “helping others” is a trait that the company values highly. It’s not really something that you can measure, though.  Managers try their best (through peer feedback, dropping by offices, etc.), but helping others usually happens below the radar. It’s not a feature. It doesn’t show up in weekly status reports. People don’t “manage it up”.

When you appreciate somebody’s help, how will the system ever know – if not for your detailed and open feedback?

Feedback in the workplace is just one example of the positive influence of feedback.

  • Blogs. If a post improves your life, post a comment! (I’ve considered locking comments to this post, because the purpose of this is emphatically not a troll!) If the whole blog makes your life better, send an email!
  • Books. Amazon comments are an author’s greatest gift. Feedback on the 1st edition of the PowerShell Cookbook has both helped me improve it, and helped others decide if the book is right for them. Feedback on the 2nd edition has just started, but is immensely helpful.
  • EBay. EBay sellers live and die by their reputation. If you’ve had a good experience, let the world know!
  • Your significant other. You’re with them because they make you happy. If you’ve been together for a while, this is easy to forget. Why not let them know that their special habit has been making your happy every day since you met?

I’m sure you can think of somebody right now that has helped you recently. Why not provide feedback? If you’d love to (but can never remember), try putting a monthly reminder in your calendar!