Using the LeanKit API with PowerShell

As I alluded to in an earlier post, I’ve been using PowerShell to interact with the LeanKit API. You can find the rationale and overarching methodology in the post linked above. Here we’ll be dealing with the nuts and bolts.

Approach 1 – Using the .NET framework provided by LeanKit (FIXED by John Mathias)

Initially I attempted to perform this task by importing the LeanKit API Client Library for .NET into PowerShell using [System.Reflection.Assembly]::LoadFile(), but ultimately couldn’t get it to authenticate successfully.

The code snippet in question is below, if anyone can point out where I went wrong, I would be most grateful.

UPDATED: Fixed! John Mathias from LeanKit was kind enough to point out that I was mistakenly using the entire URL to populate the ‘hostname’ field. Change it to using just the subdomain, and it works a treat!

$scriptWorkingDirectory = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent

# Define variables
$boardID = 01234;
$boardAddress = "subdomain"; # Your leankit subdomain, e.g. JUST the 'subdomain' part of https://subdomain.leankit.com
$boardUser = "user@example.com";

# Load LeanKit API Library dependencies
[System.Reflection.Assembly]::LoadFile("$scriptWorkingDirectory\LeanKit.API.Client.Library\LeanKit.API.Client.Library.dll") | out-null
[System.Reflection.Assembly]::LoadFile("$scriptWorkingDirectory\LeanKit.API.Client.Library\Newtonsoft.Json.dll") | out-null
[System.Reflection.Assembly]::LoadFile("$scriptWorkingDirectory\LeanKit.API.Client.Library\RestSharp.dll") | out-null

# Create Authentication object so we can feed it into our factory object's creation
$leanKitAuth = New-Object LeanKit.API.Client.Library.TransferObjects.LeanKitAccountAuth -Property @{
    Hostname = $boardAddress;
    Username = $boardUser;
    Password = $([Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($(read-host "Pass" -assecurestring))));
}

# Connect and authenticate
Write-Verbose "Connecting to $($leanKitAuth.Hostname)";
$leankitApi = $(New-Object LeanKit.API.Client.Library.LeanKitClientFactory).Create($leanKitAuth);

# Create a new card
$newCard = @{
    Title = "New card!!!";
    Description =  "Oh my yes, it's a new card!";
    TypeID=01234;
}
$newCard = New-Object LeanKit.API.Client.Library.TransferObjects.Card -Property $newCard;
$leankitApi.AddCards($boardID,[LeanKit.API.Client.Library.TransferObjects.Card[]]@($newCard), "Automation!!!")

# Get the board
$leankitBoard = $leankitAPI.GetBoard($boardID);

# Get the card we just added
$card = $leankitBoard.alllanes().cards[0];

# Convert it to a card rather than a view
$card = $card.toCard()

# Change it slightly
$card.Title = "That's no card!!!"

# Update it!
$leankitApi.UpdateCards($boardID,[LeanKit.API.Client.Library.TransferObjects.Card[]]$card,"Automation");

The above code would result in a very long wait at the final step where it would (according to Fiddler2) make several calls to a blank HTTPS address. So I can only assume that the $leanKitAuth object isn’t getting properly passed to the .Create() method.

The above code now works properly! Thanks John!
It also uses the plural versions of UpdateCards with the appropriate typing so you can pass an array of card objects when you have multiple cards to update.

Method 2 – Invoke-RestMethod

Ultimately PowerShell’s Invoke-RestMethod, is absolutely perfect for the job anyway, so I decided to use that in lieu of getting the framework working leave it here as an example even though the code above now works.

Step 1) Getting your board

I created two very basic functions in order to get a board.

Set-LeanKitAuth

function Set-LeanKitAuth{
    param(
        [parameter(mandatory=$true)]
        [string]$url,

        [parameter(mandatory=$true)]
        [System.Management.Automation.PSCredential]$credentials
    )
    $script:leanKitURL = $url;
    $script:leankitCreds = $credentials
    return true;
}

Get-LeanKitBoard

function Get-LeanKitBoard{
    param(
        [parameter(mandatory=$true)]
        [int]$BoardID
    )

    if(!($script:leanKitURL -and $script:LeanKitCreds)){
        throw "You must run set-leankitauth first"
    }

    [string]$uri = $script:leanKitURL + "/Kanban/Api/Boards/$boardID/"
    return Invoke-RestMethod -Uri $uri  -Credential $script:leankitCreds
}

The idea here is that you only have to call Set-LeanKitAuth once at the beginning of the script, then your credentials are pervasive throughout the subsequent calls.

So to use the above functions, you would have a snippet like so:

Set-LeanKitAuth -url "https://myteam.leankit.com" -credentials (Get-Credential)

$leankitBoard = Get-LeanKitBoard -BoardID 1234567890

(Obviously replacing the URL and BoardID as appropriate.)
This will prompt you for your username and password (email address and password namely), and then put the resulting board in $leanKitBoard.

Data to get you started

  • Lanes: $leanKitBoard.ReplyData.Lanes
  • Backlog: $leanKitBoard.ReplyData.BackLog
  • Lane Cards: $leanKitBoard.ReplyData.Lanes.Cards
  • Backlog Cards: $leanKitBoard.ReplyData.BackLog.Cards
  • CardTypes: $leanKitBoard.ReplyData.cardtypes

Step 2) Adding Cards

In order to add cards using PowerShell, I whipped up another function, similar to the first.

Add-LeanKitCards

function Add-LeanKitCards{

    param(
        [parameter(mandatory=$true)]
        [int]$boardID,

        [parameter(mandatory=$true)]
        [ValidateScript({
            if($_.length -gt 100){
                #"You cannot pass greater than 100 cards at a time to add-LeankitCards"
                return $false;
            }
           if(
                ($_ |?{$_.UserWipOverrideComment}).length -lt $_.length
               ){
                # "All cards must have UserWipOverrideComment when passing to Update-LeankitCards";
                return $false;
            }
            return $true;
        })]
        [hashtable[]]$cards
    )

    [string]$uri = $script:leanKitURL + "/Kanban/Api/Board/$boardID/AddCards?wipOverrideComment=Automation"
    return Invoke-RestMethod -Uri $uri  -Credential $script:leankitCreds -Method Post -Body $(ConvertTo-Json $cards ) -ContentType "application/json" 

}

This requires you to pass a hashtable (or an array of hashtables) with the appropriate values to the parameter -cards.

Here’s an example:

$cardArray = @();
$cardArray += @{
    Title = "This is a new card";
    Description =  "Oh my, so it is. A fresh card!";
    TypeID=1234567890;
    laneID=1234567890
    UserWipOverrideComment = "Automation! Yeah!"
}

 Add-LeanKitCards -boardID 1234567890 -cards $cardArray

Again, obviously, replacing the numbers with IDs meaningful to your environment (use the examples in Data to get you started above to help you find what these IDs would be in your environment).

Step 3) Updating Cards

Updating cards is a little tricker, as you must provide more data. But before we get into that, here’s the function we’ll use (almost identical to the one above).

Update-LeankitCards

function Update-LeankitCards{

    param(
        [parameter(mandatory=$true)]
        [int]$boardID,

        [parameter(mandatory=$true)]
        [ValidateScript({
            if($_.length -gt 100){
                # "You cannot pass greater than 100 cards at a time to Update-LeankitCards"
                return $false;
            }
            if(
                ($_ |?{$_.UserWipOverrideComment}).length -lt $_.length
               ){
                # "All cards must have UserWipOverrideComment when passing to Update-LeankitCards";
                return $false;
            }
             if(
                ($_ |?{$_.ID}).length -lt $_.length
               ){
                # "All cards must have an ID when passing to Update-LeankitCards";
                return $false;
            }
            return $true;
        })]
        [hashtable[]]$cards
    )

    [string]$uri = $script:leanKitURL + "/Kanban/Api/Board/$boardID/UpdateCards?wipOverrideComment=Automation"
    return Invoke-RestMethod -Uri $uri  -Credential $script:leankitCreds -Method Post -Body $(ConvertTo-Json $cards) -ContentType "application/json"
}

Obviously we’ll have to pass the card ID in the array, but while playing around with this, it seemed like you needed more than that. In the end I just created a new hashtable of all the properties of the card I’m updating and then changed the ones I wanted to update. Like so:

# Get a card from a previous board response (I'm getting the first one here with '[0]', you'll probably want to choose your card more carefully)
$card = $leanKitBoard.ReplyData.lanes.cards[0]

# Create the hashtable
$updatedCard = @{UserWipOverrideComment = "No override"};

# Populate the hashtable with all the properties of the card we selected previously.
$card | gm | ?{$_.membertype -eq "NoteProperty"} | %{$updatedCard.add($_.name, $card.$($_.name))}

# Change the parameters you want to change
$updatedCard.LaneID = 01234567890;

# Add the card to an array
$cardArray = @($updatedCard);

# Submit it!
Update-LeankitCards -boardID 1234567890 -cards $cardArray

I shouldn’t need to say it again, but obviously, change the board ID etc to reflect your environment.

And that’s it! Easy really. There’s a lot more that you can do, for which I suggest you see the additional reading section.
Any questions, just leave them in the comments.

Additional Reading

https://support.leankit.com/forums/20153741-LeanKit-API-Application-Programming-Interface-

https://support.leankit.com/entries/20265038-API-Basics

http://technet.microsoft.com/en-us/library/hh849898.aspx

Advertisements

Tagged: , ,

10 thoughts on “Using the LeanKit API with PowerShell

  1. John Mathis (@johndmathis) April 22, 2014 at 6:18 pm Reply

    Sam, nice write up! I believe the problem with your “approach 1” script is that you are providing the full url to the ‘Hostname’ property. It should be just your account name (‘team’ in the example you have above). Perhaps Hostname should be changed to ‘AccountName’ to make that clearer.

  2. Alan November 10, 2014 at 1:23 pm Reply

    First off, this example has been a great help! Thanks

    Second, I was wondering, i’m trying to utilize this in a batch process, whereby prompting for the username and password hinders the process.

    Is there a way to get around from using the System.Management.Automation.PSCredential command and just hard coding the credentials into the script?

  3. Alan November 10, 2014 at 4:14 pm Reply

    Found my solution. For anyone else looking for what i’ve done you can review this:
    http://technet.microsoft.com/en-us/magazine/ff714574.aspx

  4. martin3361 November 15, 2014 at 11:27 pm Reply

    Hi, for all three .dll (downloaded the 20141003 zip file) i tried your Method 1 up there and all Im getting is “Exception calling “LoadFile” with “1” argument(s): “This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded. (Exception from HRESULT: 0x801310
    1B)””

    programs says i have .net framework 4.5.1 ….
    thanks

  5. martin3361 November 16, 2014 at 7:24 pm Reply

    ha!
    well, thats just fab. but no matter, i actually had to upgrade the powershell to 4.0 because of the invoke-restmethod support (used in your script). and i just didnt try again after that back on the .NET API.

    i have played around with your second method mainly because im curious about how this works underneath and wanted something very quick and simple.
    and your code here is very clean and very nice!

    thanks much!

  6. martin3361 November 16, 2014 at 8:02 pm Reply

    Also, have you seen this ?
    im playing with the .net API, and i dont see where you specify on which lane to issue new tickets… where is it ? with the “raw” rest post, it is in the URL, but where do we specify the lane with this official API ?

    thanks

    • martin3361 November 16, 2014 at 8:04 pm Reply

      ugh, sorry, please ignore this post about laneID. it is in the card array…… 😐

  7. Toukakoukan November 16, 2014 at 9:16 pm Reply

    No worries!
    Whenever I was figuring out how to do something I just had a search through the source.
    https://github.com/LeanKit/LeanKit.API.Client
    Which may well have been where you found your answer 🙂
    Also get-member is really useful (particularly in a PowerShell ISE window for persistence with your script) as you can find out all the methods and properties for a given object type.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: