title: Invoking callbacks from PowerShell functions
body: <p>I'm trying to add some callback capabilities to a PowerShell function but I want to be able to define the behaviour of the callbacks outside of the function definition, so basically a very simple Inversion of Control for PowerShell.</p>
  
  <p>I'm trying to be as backward compatible as is practical, and this is what I've come up with so far (error handling removed for brevity):</p>
  
  <pre><code>$ErrorActionPreference = "Stop";
  Set-StrictMode -Version "Latest";
  
  function Invoke-DoSomethingMethod
  {
      param
      (
          [hashtable] $Callbacks
      )
      $CallBacks["OnStarted"].Invoke();
      for( $i = 1; $i -le 10; $i++ )
      {
          # ... do something here ...
          $CallBacks["OnProgress"].Invoke($i * 10);
      }
      $CallBacks["OnFinished"].Invoke();
  }
  
  $myHostCallbacks = @{
      "OnStarted"  = { write-host "started" }
      "OnProgress" = { write-host "processing $($args[0])%" }
      "OnFinished" = { write-host "finished" }
  }
  
  $myFileCallbacks = @{
      "OnStarted"  = { Add-Content "C:\temp\log.txt" "started" }
      "OnProgress" = { Add-Content "C:\temp\log.txt" "processing $($args[0])%" }
      "OnFinished" = { Add-Content "C:\temp\log.txt" "finished" }
  }
  
  $myWebCallbacks = @{
      "OnStarted"  = { Invoke-WebRequest -Uri "http://example.org/OnStarted" }
      "OnProgress" = { Invoke-WebRequest -Uri "http://example.org/OnProgress/$($args[0])" }
      "OnFinished" = { Invoke-WebRequest -Uri "http://example.org/OnFinish" }
  }
  
  Invoke-DoSomethingMethod -Callbacks $myHostCallbacks;
  </code></pre>
  
  <p>When run, this outputs the following:</p>
  
  <pre><code>c:\temp&gt;powershell .\callbacks.ps1
  
  started
  processing 10%
  processing 20%
  processing 30%
  processing 40%
  processing 50%
  processing 60%
  processing 70%
  processing 80%
  processing 90%
  processing 100%
  finished
  </code></pre>
  
  <p>Without using external modules or C# code, is there a more idomatic way of doing this with PowerShell that doesn't rely on matching up the magic strings for callback names and the order of $args[] parameters? (Preferrably compatible with version 2.0).</p>
  
  <p>Cheers,</p>
  
  <p>Mike</p>
  
  <p><strong>Update</strong></p>
  
  <p>For some context, my Invoke-DoSomething function is part of a CI build framework
  (<a href="https://github.com/ASOS/OctopusStepTemplateCi" rel="nofollow noreferrer">https://github.com/ASOS/OctopusStepTemplateCi</a>) which is currently hard-coded to send TeamCity service messages using Write-Host during the build process.</p>
  
  <p>We're interested in supporting other build server products so I'm investigating a way to abstract out the various interactions with the build server into user-definable callbacks like "OnBuildStarted", "OnBuildStepFailed", etc, like you might do with an IoC framework in C#.</p>
  
  <p>This would make it easier to ship support for new build servers from within the project, and also for users to write callbacks for build servers which don't come out-of-the-box.</p>
  
  <p>This might help give a flavour of what I'm trying to do:</p>
  
  <pre><code>function Invoke-BuildScript
  {
      param( [hashtable] $Callbacks )
      $CallBacks["OnBuildStarted"].Invoke();
      ... build script here ...
      $CallBacks["OnBuildFinished"].Invoke();
  }
  
  $teamCityCallbacks = @{
      "OnBuildStarted"  = { write-host "started" }
      "OnBuildFinished" = { write-host "finished" }
      "OnBuildError"    = { write-host "error '$($args[0])'" }
  }
  
  $jenkinsCallbacks = @{
      "OnBuildStarted"  = { ... }
      "OnBuildFinished" = { ... }
      "OnBuildError"    = { ... }
  }
  
  Invoke-BuildScript -Callbacks $teamCityCallbacks;
  </code></pre>