Post

Powershell Functions: Alias and InvocationName

Introduction

When working on a PowerShell module for querying an api, someone pointed out to me that there were some functions within this module that were actually pretty similar, though not completely the same. I had considered it too, but merging these functions into one functions and using parameters (several switch statements or so), felt clunky to say the least. So I got to thinking what would be a more tidy solution to this problem?

The problem

Lets start by introducing a simplified example of what the problem looked like. So imagine this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function Get-ItemFromRest {
  Param(
    [string]$itemId
  )

  return(_invokeRestRequest -item ("MyItem/{0}" -f $itemId))
}

function Get-ItemSomeSubResource1FromRest {
  Param(
    [string]$itemId
  )

  return(_invokeRestRequest -item ("MyItem/{0}/SomeSubResource1" -f $itemId))
}

function Get-ItemSomeSubResource2FromRest {
  Param(
    [string]$itemId
  )

  return(_invokeRestRequest -item ("MyItem/{0}/SomeSubResource2" -f $itemId))
}

The (fictional) function _invokeRestRequest is an module internal function which build the request uri, sets the headers for authentication and so on, returning the result(s) from the REST api. This already helps making a module like this a lot more terse, allowing the functions that will get exported to be a lot smaller.

Granted however, the three functions do share a lot of code.

Not such a nice solution

As I said, originally I already considered that there was some similarity but at the time the only solution I could see was using switch parameters to change the behaviour. Something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Get-ItemFromRest {
  Param(
    [string]$itemId,
    [switch]$SomeSubResource1,
    [switch]$SomeSubResource2
  )

  If ($SomeSubResource1) {
    $itemRequest = ("MyItem/{0}/SomeSubResource1" -f $itemId)
  } elseif ($SomeSubResource2){
    $itemRequest = ("MyItem/{0}/SomeSubResource2" -f $itemId)
  } else {
    $itemRequest = ("MyItem/{0}" -f $itemId)
  }

  return (_invokeRestRequest -item $itemRequest)
}

Yes I realise this can be improved. Like using parameter sets to prevent both subresource switches to be set at the same time and probably many other things. But for brevity, lets ignore that.

Would it work? Yes, of course it would:

  • Get-ItemFromRest -itemId ‘1234567’
  • Get-ItemFromRest -itemId ‘1234567’ -SomeSubResource1
  • Get-ItemFromRest -itemId ‘1234567’ -SomeSubResource2

All of them would work and would return the correct results. But it doesn’t feel right. Not really (to me, anyway).

A better solution, using Aliases

I already knew that I could use an alias to allow one function to have multiple useable names. However, would it be possible to determine how we were called? And change behavior depending on the way we are called?

Turns out, yes you can! And it really is not that complicated either…

So lets try this again:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Get-ItemFromRest {
  [Alias("Get-ItemSomeSubResource1FromRest")]
  [Alias("Get-ItemSomeSubResource2FromRest")]
  Param(
    [string]$itemId
  )

  Switch ($MyInvocation.InvocationName){
    "Get-ItemSomeSubResource1FromRest" {
      $itemRequest = ("MyItem/{0}/SomeSubResource1" -f $itemId)
    }
    "Get-ItemSomeSubResource2FromRest" {
      $itemRequest = ("MyItem/{0}/SomeSubResource2" -f $itemId)
    }
    default {
      $itemRequest = ("MyItem/{0}" -f $itemId)
    }
  }

  return (_invokeRestRequest -item $itemRequest) 
}

Is it more terse than the ‘Not such a nice solution’? Well, probably it isn’t. But for me it feels better to have the consumer of the module to be able to just use a cmdlet rather than specifying a switch to change behavior. In this case that would be:

  • Get-ItemFromRest -itemId ‘1234567’
  • Get-ItemSomeSubResource1FromRest -itemId ‘1234567’
  • Get-ItemSomeSubResource2FromRes -itemId ‘1234567’

So in the end, it still allows us to merge 3 functions into one. And this way it feels rather ok as well.

$MyInvocation is part of PowerShell automatic variables. Some of these I know, others I have never used (as of yet). In any case, more information on MyInvocation can be found here: Automatic Variables.

This post is licensed under CC BY 4.0 by the author.