Solving Programming Problems with PowerShell (Problem 4)

I recently read a blog post by Santiago Valdarrama about developer programming problems. He said that any programmer should be able to solve five problems in an hour or less with a programming language of his choice. My language of choice is PowerShell, which is probably not what he had in mind when he wrote the blog post. So far, I’ve demonstrated In adding numbers in an array three different ways, merging lists together and solving the Fibonacci series.

Problem 4: The Maximal Combination

Problem 4 is described thusly:

Write a function that given a list of non negative integers, arranges them such that they form the largest possible number. For example, given [50, 2, 1, 9], the largest formed number is 95021.

This one got me thinking. PowerShell has a cmdlet for sorting, but it won’t do what I want it to – it will sort numerically or lexically in ascending or descending mode. This isn’t flexible enough for my purposes. At the heart of every sort operation is a comparator – two elements in the list will be compared and one will be shown to be “before” the other. For example, 8 is before 9 (when sorting ascending).

Now, let’s take our problem. In order to compare two numbers, we have to look at their combination. Given 5 and 50, 5 comes before 50 because 5-50 is bigger than 50-5 when you push them together. We need to encode that logic in our comparator.

Fortunately, we have the full power of .NET at our disposal. Explicitly, there is a sort method on the System.Collections.ArrayList object that has a Sort method that takes a custom IComparer object. Every sort operation ultimately compares two things in the list – the IComparer interface allows us to specify a custom ordering. First of all, we need to get an ArrayList of strings. Let’s take a look at the code in two pieces:

#
# Custom sort routine for the ArrayList
#
$comparatorCode = @"
using System.Collections;

namespace Problem4 {
    public class P4Sort: IComparer {
        public static void Sorter(System.Collections.ArrayList foo) {
            foo.Sort(new P4Sort());
        }

        public int Compare(object x, object y) {
            string v1 = (string)x + (string)y;
            string v2 = (string)y + (string)x;

            return v2.CompareTo(v1);
        }
    }
}
"@

Add-Type -TypeDefinition $comparatorCode

The Compare method is used to compare our two numbers. The Sorter method is a static method that will do an in-place sort of the provided ArrayList usign the custom comparator. A quick note – you can only add this type once. You will likely have to restart your PowerShell session if you make changes to it. Now, let’s look at my cmdlet:

function ConvertTo-BiggestNumber
{
    [CmdletBinding()]
    [OutputType([string])]
    Param
    (
        # Param1 help description
        [Parameter(Mandatory=$true, Position=0)]
        [int[]] $Array
    )

    Begin
    {
        $stringArray = new-object System.Collections.ArrayList

        # Convert the original list to an arraylist of strings
        for ($i = 0 ; $i -lt $Array.Length ; $i++) {
            $stringArray.Add($Array[$i].toString()) | Out-Null
        }
    }
    Process
    {
        [Problem4.P4Sort]::Sorter($stringArray)
        [string]::join("", $stringArray.ToArray())
    }
}

This starts by converting the array we are provided to a string ArrayList as required by our custom type, and then sorts it using the .NET Framework Sort method we imported using Add-Type. Finally, we join the array list together. Use it like this:

$a = @( 60, 2, 1, 9)
ConvertTo-BiggestNumber -Array $a

You will get the output 96021.

That leaves the fifth puzzle. Unfortunately, I was not able to find a neat solution in PowerShell to the fifth problem. I ended up dropping down to embedded C# – my solution came pretty close to the authors solution to the same problem. Whether this one is a suitable question on an interview is an open debate – I contest that this doesn’t actually test programming skills but rather logic skills. Given the problem, you either see how to do it or you don’t. If you don’t then no amount of coding skills is going to solve the problem.

Solving Programming Problems with PowerShell (Problem 3)

I recently read a blog post by Santiago Valdarrama about developer programming problems. He said that any programmer should be able to solve five problems in an hour or less with a programming language of his choice. My language of choice is PowerShell, which is probably not what he had in mind when he wrote the blog post. So far, I’ve demonstrated In adding numbers in an array three different ways and merging lists together. Problem 3 is a classic – the Fibonacci Series Calculation.

Problem 3: Fibonacci Numbers

The Fibonacci Series is a classic numerical problem. The first two numbers in the series are 0 and 1. After that, the next element is the sum of the previous two numbers. The problem statement is this:

Write a function that computes the list of the first 100 Fibonacci numbers. By definition, the first two numbers in the Fibonacci sequence are 0 and 1, and each subsequent number is the sum of the previous two. As an example, here are the first 10 Fibonacci numbers: 0, 1, 1, 2, 3, 5, 8, 13, 21, and 34.

There are two ways of doing this – recursion and a simple loop. I always avoid recursion if I can – it can so easily go wrong. So I’ll do a loop method. I also will not use an array – just two numbers.

Here is my code:

function New-FibonacciSeries
{
    [CmdletBinding()]
    [Alias()]
    [OutputType([int])]
    Param
    (
        # Number of numbers in the series to produce
        [Parameter()]
        $Limit = 100
    )

    Begin
    {
        if ($Limit -le 0) {
            throw "Cannot produce negative number of numbers"
        }
    }
    Process
    {
        $n1 = 0;
        $n2 = 1;

        $i = 0;
        while ($i -lt $Limit) {
            if ($i -eq 0) {
                $n1 | Write-Output
            } elseif ($i -eq 1) {
                $n2 | Write-Output
            } else {
                $new = $n2 + $n1
                $n1 = $n2
                $n2 = $new
                $new | Write-Output
            }
            $i += 1
        }
    }
}

Here, we have to special case the first and second times through the loop because there isn’t enough data to complete the next step. Once we do get beyond there, we can use the formula. For reference, the 100’th element is 2.18922995834555E+20 (a very large number) – the numbers escalate ridiculously quickly.

Solving Programming Problems with PowerShell (Problem 2)

I recently read a blog post by Santiago Valdarrama about developer programming problems. He said that any programmer should be able to solve five problems in an hour or less with a programming language of his choice. My language of choice is PowerShell, which is probably not what he had in mind when he wrote the blog post. In the last article, I demonstrated the PowerShell solution to the first problem – adding numbers in a list multiple different ways. So what about the second problem.

Problem 2: Merging Lists

The second problem is this:

Write a function that combines two lists by alternately taking elements from each list. For example: given the two lists [a, b, c] and [1, 2, 3], the function should return [a, 1, b, 2, c, 3].

This sounds simple enough, but what if the lists are different lengths? The way that I opted to solve this is that the rest of the elements should get appended to the end of the combined list. Let’s take a look at the set-up for this one:

$list1 = @( "a", "b", "c" )
$list2 = @( "1", "2", "3" )
Merge-Lists -ListA $list1 -ListB $list2

This should print out six lines – a, 1, b, 2, c, 3. The script for this is as simple as the first problem:

function Merge-Lists
{
    [CmdletBinding()]
    Param
    (
        # The First List
        [Parameter()]
        [System.Collections.ArrayList] $ListA,

        # The First List
        [Parameter()]
        [System.Collections.ArrayList] $ListB
    )

    Process
    {
        $i = 0;
        while ($i -lt $ListA.Count -and $i -lt $ListB.Count) {
            if ($i -lt $ListA.Count) {
                $ListA[$i] | Write-Output
            }
            if ($i -lt $ListB.Count) {
                $ListB[$i] | Write-Output
            }
            $i = $i + 1
        }
    }
}

This technically does the job. However, we can actually do better from a functionality basis. Let’s say I want to use a slightly different syntax. Instead of passing in the first array as an argument, maybe I want to pass it in as the pipeline. Something like:

$list1 | Merge-WithList -With $list2

In addition, I want to use parameters within the same function:

Merge-WithList -Source $list1 -With $list2

We can actually support both of these cases in one function. Here it is:

function Merge-WithList
{
    [CmdletBinding()]
    Param
    (
        # The First List
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)]
        [System.Collections.ArrayList] $Source,

        # The First List
        [Parameter()]
        [System.Collections.ArrayList] $With
    )

    Begin
    {
        $i = 0
    }
    Process
    {
        foreach ($s in $Source) {
            $s | Write-Output
            if ($i -lt $With.Count) {
                $With[$i] | Write-Output
            }
            $i += 1
        }
    }
    End
    {
        While ($i -lt $With.Count) {
            $With[$i] | Write-Output
            $i += 1
        }
    }
}

In this version we use the ValueFromPipeline to feed the array into the $Source variable. I’ve also put the initialization in the Begin block and the finalization (in case the With array is longer than the source array) in the End block.

Solving Programming Problems with PowerShell (Problem 1)

I recently read a blog post by Santiago Valdarrama about developer programming problems. He said that any programmer should be able to solve five problems in an hour or less with a programming language of his choice. My language of choice is PowerShell, which is probably not what he had in mind when he wrote the blog post. Let’s see how I get on.

Problem 1: Adding Numbers

This seemed a little trivial for a problem, but let’s see how it goes with PowerShell. The problem is this:

Write three functions that compute the sum of the numbers in a given list using a for-loop, a while-loop, and recursion.

To do this, I’m going to write the script like so:

$array = @(1, 2, 3, 4)

Add-NumbersWithForLoop -Numbers $array
Add-NumbersWithWhileLoop -Numbers $array
Add-NumbersWithRecursions -Numbers $array

Once I’m done, the script will print 10 three times. In addition, I’m going to write PowerShell advanced cmdlets so that I’m demonstrating the “best practice” of writing scripts. Let’s take a look at the first one – the for-loop:

function Add-NumbersWithForLoop
{
    [CmdletBinding()]
    [Alias()]
    [OutputType([int])]
    Param
    (
        # List of numbers to add
        [Parameter(Mandatory=$true)]
        [int[]] $Numbers
    )

    Begin
    {
        $Total = 0
    }
    Process
    {
        for ($i = 0 ; $i -lt $Numbers.Length ; $i++) {
            $Total += $Numbers[$i]
        }
    }
    End
    {
        $Total | Write-Output
    }
}

This is possibly the simplest of the three versions. I just cycle through the array, adding up numbers. If I don’t supply an array or the array isn’t integers, the in-build error checking will work. This also works if there are no entries in the list then the total will be zero – there is no error there. The while-loop needs error checking for this though:

function Add-NumbersWithWhileLoop
{
    [CmdletBinding()]
    [Alias()]
    [OutputType([int])]
    Param
    (
        # List of numbers to add
        [Parameter(Mandatory=$true)]
        [int[]] $Numbers
    )

    Begin
    {
        $Total = 0
    }
    Process
    {
        if ($Numbers.Length -gt 0) {
            $i = $Numbers.Length - 1
            while ($i -ge 0) {
                $Total += $Numbers[$i]
                $i -= 1
            }
        }
    }
    End
    {
        $Total | Write-Output
    }
}

In this case, we start at the last element and go down until we hit $Numbers[-1] – at which point, we abort the loop. Technically, I don’t need the error-check on the $Numbers.Length, but it’s a good practice to be explicit about your error checking. Don’t leave error checking to rely on default behavior – that has a habit of changing over time. The final one is recursion:

function Add-NumbersWithRecursion
{
    [CmdletBinding()]
    [Alias()]
    [OutputType([int])]
    Param
    (
        # List of numbers to add
        [Parameter(Mandatory=$true)]
        [int[]] $Numbers
    )

    Process
    {
        if ($Numbers.Length -eq 0) {
            reutrn 0
        } elseif ($Numbers.Length -eq 1) {
            return $Numbers[0]
        } else {
            return $Numbers[0] + (Add-NumbersWithRecursion -Numbers $Numbers[1..$Numbers.Length])
        }
    }
}

For recursion, I’m taking a slice of the array we are passed from the second element (remember – elements are numbered from 0) to the end and adding the total of that to the first element. Recursion needs an “out” or termination clause. In this case, if there is just one element, then I return that element and if there are no elements, I return 0.

This is the first problem in a series of five. Next time, I’ll tackle the second problem.

Publishing to Azure – Node Style

In the last five articles I’ve been doing a study of what it takes to do a node app. For those who are starting here, I had a list of requirements:

  1. I need to be able to run a small web server
  2. I need to be able to handle templated views with server-side code
  3. I need to be able to do social authentication
  4. I need to be able to use an MVC architecture
  5. I need to be able to provide a Web API
  6. I need to be able to publish a node app to Azure
  7. I need to be able to edit node applications in Visual Studio 2015

Today is all about publishing my project to an Azure Website. I’ve actually got a reasonable web application already. It is an MVC structure, has a Web API and is authenticated through one of a number of services thanks to a plug-in from Auth0.

There is a great article on publishing to Azure already. Unfortunately, it relies on you starting from scratch. I’ve already got a directory full of stuff, so how shall I publish something that I already have?

Step 0: Install Azure PowerShell

This step is a one-off. Microsoft has created a set of PowerShell cmdlets specifically for Azure. It’s a Web PI installer. Just go get it and install it.

Step 1: Define the Cloud Service

The first step is to define a cloud service. Microsoft would have you run New-AzureServiceProject, which is ok, but requires that you specify a directory that doesn’t exist. My project exists already. Fortunately, the New-AzureServiceProject cmdlet does nothing more than create four basically empty XML files. You can use New-AzureServiceProject emptyapp to create the empty files, then edit them.

In basic-webapp, I’ve created the four files. The first two are copies of one another: ServiceConfiguration.Cloud.cscfg and ServiceConfiguration.Local.cscfg:

<?xml version="1.0" encoding="utf-8"?>
<ServiceConfiguration xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" serviceName="basic-webapp" osFamily="2" osVersion="*" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" />

The ServiceDefinition.csdef file is similar – just different XML tags:

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="basic-webapp" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition" />

Note that the New-AzureServiceProject cmdlet generates UTF-16 files. I find UTF-8 files easier to edit, especially in Atom, which is what I am using right now. I just altered the encoding in the xml header to be utf-8 instead of utf-16 when I was cutting and pasting.

Finally, the deploymentSettings.json is the same for all projects at this stage. You can just cut and paste it in:

{"Slot":"","Location":"","Subscription":"","StorageServiceName":"","AffinityGroup":""}

Once I have initialized the project, I can add a web worker role. This is done by another PowerShell cmdlet: Add-AzureNodeWebRole.

blogcode-0525-1

This will update the Service Configuration files (and convert them back to UTF-16, so don’t save them in Atom after you have created them).

Side bar: It’s important that your server reads the TCP port number from process.env.PORT if it is available. Azure requires this logic. I already built this logic into the server as I developed it.

Step 2: Move the Code into WebRole1

The prior step created a directory WebRole1 with a whole bunch of stuff in it. You need to move all your code into the WebRole1 directory. That includes everything that has been produced thus far.

Once you have finished you should have only the four files you created in Step 1, plus the WebRole1 directory at the top level:

blog-code-0525-2

Step 3: Adjust URLs to Match Environment

I’m going to deploy my application with the name ShellmongerNodeExample. That means that it will appear as http://shellmongernodeexample.cloudapp.net/ on the web. This URL is located in two places. Firstly, it’s in the config.json file in the “uri” section:

{
  "loginRoute": "/account/login",
  "server": {
    "uri": "http://shellmongernodeexample.cloudapp.net",
    "port": 80
  },

Secondly, it’s in the Auth0 management portal under the callback URL:

blog-code-0525-5

You will need to do both changes in order for Social Authentication to work.

Step 4: Deploy the Application to Azure

At this point you will need an Azure subscription. You can sign up for a free trial, use MSDN activated credentials or just activate a pay-as-you-go subscription. Whichever way you do it, you need Azure credentials.

Run Get-AzurePublishSettingsFile. This will bring up a web browser, ask you to log in with your Azure credentials and then finally download a file called something.publishsettings. Store this somewhere secure and whatever you do, don’t check it into GitHub. Consider it an opaque blob that is required to publish stuff to Azure.

To use it, you need to import it:

Import-AzurePublishSettingsFile ~\Downloads\my.publishsettings

Replace the filename with the one you just downloaded.

To publish your Node application to Azure, you can now do the following:

Publish-AzureServiceProject -ServiceName ShellmongerNodeExample -Location "West US" -Launch

Note that the ServiceName does not need to be the same as your project name. In fact, it’s unlikely that using the same name would work because of conflicts. This process takes a few minutes, but it prints out what it is doing along the way. Here is what mine looked like:

blog-code-0525-3

If you browse to portal.azure.com and log in, you will see the application running:

blog-code-0525-4

Note that a storage account and a cloud app are created. You can also browse to the app (the Publish activity should have opened a browser window for you). If you’ve done Step 3 properly, you’ll be able to log in via one of the Social connectors and see the home page. You’ll also be able to use Postman to check out the Web API.

Starting and Stopping an Azure Service

I’m a bit fickle about the resources I consume in the cloud. There are two commands you want to know:

  1. Start-AzureService starts the service
  2. Stop-AzureService stops the service

Each one takes a -ServiceName parameter – running it in your project directory will start/stop your application. Running these commands outside the project directory requires the -ServiceName parameter.

Deleting the Azure Service

There are two components to the azure service – a cloud app (the service) and a storage account. If you have followed these instructions, the name of the storage account will be the same as the cloud app. However, you may have adjusted it – so these instructions may not work for you, unless you followed the above information.

Firstly, stop your Azure Service with Stop-AzureService. Then you can delete each resource in turn:

Remove-AzureService -ServiceName ShellmongerNodeExample -Verbose
Get-AzureStorageAccount | ? Label -eq "ShellmongerNodeExample" | Remove-AzureStorageAccount

WARNING: There is no going back here – once the storage account is gone, you cannot recreate it to get the data back. You have been warned.

Closing Up

I didn’t get the Azure Emulator to work. This might be because I didn’t create the node application in the “Azure recommended way”, or it may be because I am running Windows 10, or it may be something all together different.

As always, the current source code is on my GitHub Repository.

PowerShell and Profiles

I showed off my PathUtils module in my previous article. Today I’m going to show off my profile. Every time you execute a PowerShell prompt or start up ISE, four profiles get run.

The four profiles are:

&gt; $Profile.AllUsersAllHosts
C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1

&gt; $Profile.AllUsersCurrentHost
C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShellISE_profile.ps1

&gt; $Profile.CurrentUserAllHosts
H:\Documents\WindowsPowerShell\profile.ps1

&gt; $Profile.CurrentUserCurrentHost
H:\Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1

When you edit or run $profile you actually edit or run the last one. I put most of my profile in the third one – profile.ps1. This is common to both the PowerShell prompt and the ISE. Then I only need to put the differences in the ISE or PowerShell prompt one.

Let’s take a look at my Microsoft.PowerShellISE_profile.ps1 file first:

Set-Location H:

function edit {
    param($file, $force = $false);

    if ($force) {
        if (-not (Test-Path $file)) {
            New-Item -ItemType File -Path $file
        }
    }

    psedit $file
}

My “home” is on my H: drive – it’s a Synology Diskstation in my basement. I change the location of documents, etc. to it and then sync the contents so that they are available off-line if I am using a laptop. Then I define a function “edit” – this creates the file I want to edit if it doesn’t exist (and I use -force) and then opens it in the ISE editor.

My Microsoft_PowerShell_profile.ps1 is similar:

Set-Location H:

Set-Alias edit atom

Instead of the ISE Editor, I’m using atom. Aside from that, this also doesn’t do much. All the work of my profile is done in profile.ps1. Here is the top of it:

Import-Module PathUtils

Add-Path -Directory &quot;${env:ProfileFiles(x86)}\PuTTY&quot;
Add-Path -Directory &quot;${env:USERPROFILE}\AppData\Roaming\npm&quot;

This just sets up my Path. I’ve added PuTTY and the NPM area to my path. Next comes git setup:

Import-Module posh-git

Add-Path -Directory &quot;${env:ProgramFiles(x86)}\Git\bin&quot;

function global:prompt {
    $realLASTEXITCODE = $LASTEXITCODE
    $Host.UI.RawUI.ForegroundColor = $GitPromptSettings.DefaultForegroundColor
    Write-Host($pwd.ProviderPath) -nonewline
    Write-VcsStatus
    $global:LASTEXITCODE = $realLASTEXITCODE
    return &quot;&amp;gt; &quot;
}

Enable-GitColors
Start-SshAgent -Quiet

posh-git was introduced last time. It’s a module that provides a colorized prompt when you are in a git repository. This is very useful when it comes to development.

I then have a series of functions that have helped me as a developer:

function Edit-HostsFile {
    Start-Process -FilePath atom -ArgumentList &quot;${env:windir}\System32\drivers\etc\hosts&quot;
}
 
function rdp ($ip) {
    Start-Process -FilePath mstsc -ArgumentList &quot;/admin /w:1024 /h:768 /v:$ip&quot;
}
 
function tail ($file) {
    Get-Content $file -Wait
}
 
function whoami {
    [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
}

function Get-ProcessorArchitecture {
    if ([System.IntPtr]::Size -eq 8) { return &quot;x64&quot; }
    else { return &quot;x86&quot; }
}

function Test-Port {
    [cmdletbinding()]
    param(
        [parameter(mandatory=$true)]
        [string]$Target,

        [parameter(mandatory=$true)]
        [int32]$Port,

        [int32]$Timeout=2000
    )

    $outputobj = New-Object -TypeName PSobject
    $outputobj | Add-Member -MemberType NoteProperty -Name TargetHostName -Value $Target
    if (Test-Connection -ComputerName $Target -Count 2) {
        $outputobj | Add-Member -MemberType NoteProperty -Name TargetHostStatus -Value &quot;ONLINE&quot;
    } else {
        $outputobj | Add-Member -MemberType NoteProperty -Name TargetHostStatus -Value &quot;OFFLINE&quot;
    }            
    $outputobj | Add-Member -MemberType NoteProperty -Name PortNumber -Value $Port
    
    $Socket=New-Object System.Net.Sockets.TCPClient
    $Connection=$Socket.BeginConnect($Target,$Port,$null,$null)
    $Connection.AsyncWaitHandle.WaitOne($timeout,$false) | Out-Null
    if($Socket.Connected -eq $true) {
        $outputobj | Add-Member -MemberType NoteProperty -Name ConnectionStatus -Value &quot;Success&quot;
    } else {
        $outputobj | Add-Member -MemberType NoteProperty -Name ConnectionStatus -Value &quot;Failed&quot;
    }            
    $Socket.Close | Out-Null
    $outputobj | 
        Select TargetHostName, TargetHostStatus, PortNumber, Connectionstatus | 
        Format-Table -AutoSize
}

All of these came from someone else. I recommend poshcode.org – it’s got lots of good scripts in there that you can include in your profile.

The other thing to note about PowerShell is that there are lots of modules available. Some of them are really obvious – for example, if you are using Azure then you will want the Azure cmdlets which are contained in – you guessed it – the Azure module. However, I have a couple of modules I have found that are particularly useful.

  1. posh-git improves your git experience
  2. PowerShell Community Extensions (or PSCX) is a collection of useful cmdlets
  3. Carbon is another collection, targeted at devops

Funnily enough, I don’t tend to put pscx and carbon on my dev machines. However, I have an active lab build environment and those two modules end up on every Windows production box as part of the build.

I’m sure I could make my life even easier in PowerShell as a developer. However, this profile and these modules provide an excellent base for my continuing development work.

PowerShell and PATH

If you’ve been using PowerShell for any length of time, you have definitely set up your profile. Mine is fairly straight forward. It’s stored in $profile, which in my case is H:\Documents\WindowsPowerShell\Microsoft.PowerShell_Profile.ps1 – an obnoxiously long name. I’d rather it be profile.ps1, but I digress.

Import-Module PathUtils
Import-Module posh-git

Add-Path -Directory "${env:ProgramFiles(x86)}\Git\bin"
Add-Path -Directory "${env:USERPROFILE}\AppData\Roaming\npm"

function global:prompt {
    $realLASTEXITCODE = $LASTEXITCODE
    $Host.UI.RawUI.ForegroundColor = $GitPromptSettings.DefaultForegroundColor
    Write-Host($pwd.ProviderPath) -nonewline
    Write-VcsStatus
    $global:LASTEXITCODE = $realLASTEXITCODE
    return "> "
}

Enable-GitColors
Start-SshAgent -Quiet

The posh-git module is for colorizing my prompt, and everything after the Add-Path cmdlets is associated with that – actually, it’s boilerplate from their example. The Add-Path is the bit of code that I wrote. It’s in a module called PathUtils and contains just one function:

function Add-Path {
  <#
    .SYNOPSIS
      Adds a Directory to the Current Path
    .DESCRIPTION
      Add a directory to the current path.  This is useful for
      temporary changes to the path or, when run from your
      profile, for adjusting the path within your powershell
      prompt.
    .EXAMPLE
      Add-Path -Directory "C:\Program Files\Notepad++"
    .PARAMETER Directory
      The name of the directory to add to the current path.
  #>

  [CmdletBinding()]
  param (
    [Parameter(
      Mandatory=$True,
      ValueFromPipeline=$True,
      ValueFromPipelineByPropertyName=$True,
      HelpMessage='What directory would you like to add?')]
    [Alias('dir')]
    [string[]]$Directory
  )

  PROCESS {
    $Path = $env:PATH.Split(';')

    foreach ($dir in $Directory) {
      if ($Path -contains $dir) {
        Write-Verbose "$dir is already present in PATH"
      } else {
        if (-not (Test-Path $dir)) {
          Write-Verbose "$dir does not exist in the filesystem"
        } else {
          $Path += $dir
        }
      }
    }

    $env:PATH = [String]::Join(';', $Path)
  }
}

Export-ModuleMember -Function Add-Path

This is fairly straight forward, but I’ve made it as all-encompassing as I could. You just use Add-Path <directory> and it will add that to the path. However, it’s not a permanent change, and that recently caused me a problem.

When Visual Studio (or any other program, for that matter) kicks off a command like bower or npm (or more appropriately, jspm, in my case), it does so in a cmd prompt – not a PowerShell prompt. The Add-Path cmdlet sets up a PowerShell only prompt. So I need to store my PowerShell prompt as the user path, permanently.

To do this, I use the following bit of code:

[Environment]::SetEnvironmentVariable("Path", $env:Path,[System.EnvironmentVariableTarget]::User)

This uses the .NET Framework to store the current path in the users environment. You can replace the ::User with ::Machine to store the path variable in the system environment, which is useful when you have just installed a new program.

Maybe I should write a Save-Path cmdlet as well?