Microsoft Planner is a nice project planning tool which still has some shortcomings but also a lot of benefits and we are giving it a try. However there are two topics somewhat hampering our progress: To the best of my knowledge there is no way to backup a plan and also no possibility to have something like a „template“ plan which you could then use to populate new plans with buckets and tasks. The latter is the number two topic if you look at the top ideas in the planner uservoice forum1 while the former has no real traction there, so it might be something only we need. But then, with a simple ex- and import functionality both could be addressed: It could be used as template functionality if you do an ex- and immediate import of a plan and it could be used as backup if you take the result of the export and just store it somewhere. Seems easy and straightforward enough, and it actually is:
Planner itself doesn’t have an API but with Microsoft’s general strategy of integration everything in their Graph API, that makes a lot of sense. There you will find a part about planner but be warned that this is currently in beta, which means it is a preview and subject to change. If you are willing to accept that, you basically need to take the following steps:
If implemented the tool in C# / .NET Core using VS Code, so if you want to look at the source, try it yourself or even send my a pull request, just go to https://github.com/tfenster/plannerexandimport.
The AAD application is used to connect to the Graph API. For a quickstart on the topic, see this nice documentation. It will need the permission to read groups (Group.Read.All or „Read all groups“) and write to groups (Group.ReadWrite.All or „Read and write all groups“), but only as delegated permissions, which means that the application can only see and create information and data which the logged in user would also see and be able to create. The application has an ID which you need to provide when authenticating with AAD.
As I wrote, the application itself can’t access anything but instead only gets delegated permissions of the logged in user. That means that we also need to log in and get a token which together with the application will allow access to the Graph API. There are a number of possible ways to achieve that but as I wanted my app to be as capable of being automated and as platform independent as reasonably possible, I decided to go with a console based approach: The sample here shows you how it is done and as I only adapted that to my needs, I won’t go into too much detail here (see https://github.com/tfenster/PlannerExAndImport/blob/master/Planner.cs#L205-L231 if you want the code). The basic approach from a user perspective is that you get a URL and a token, you open that URL, enter the token and allow the application to work with your credentials. That works very reliably.
Now we have everything in place to access the Graph API, so let’s get groups and plans: First we need an HTTP client that we connect to https://graph.microsoft.com/beta/groups and add the auth token as bearer token in the authorization request header (see https://github.com/tfenster/PlannerExAndImport/blob/master/Planner.cs#L191-L203). Then we can query the endpoint for groups, e.g. by name. I’ve implemented this by using the amazing „Paste JSON as Code“ extension for VS Code which allows you to copy some JSON and then paste C# code you can use to get (de)serialize it. I implemented a generic GraphRepsonse class (see https://github.com/tfenster/PlannerExAndImport/blob/master/JSON/GraphResponse.cs) to further reduce the need to create specific code for the different requests and responses. With that in place, the implementation is basically two lines: ask the user for the name and then do a GET request with some filtering (see https://github.com/tfenster/PlannerExAndImport/blob/master/Planner.cs#L144-L145). As we might have multiple matches, the user gets a list of results and can select the right one. With the id of that group, we then query the plans endpoint, get a list of all plans (see https://github.com/tfenster/PlannerExAndImport/blob/master/Planner.cs#L165) and then again ask the user to select the right one.
With all that preparation done we can now finally do what we set out to in the beginning: Export the content of our plan. The buckets can be queried through „plans/<plan-id>/buckets“ and the tasks with „plans/<plan-id>/tasks“. We also need the task details from „tasks/<task-id>/details“ for the check lists inside of tasks (see https://github.com/tfenster/PlannerExAndImport/blob/master/Planner.cs#L26-L54). As I find it easier to handle in json that way, I put everything into a tree structure with tasks below buckets and task details below tasks. After that we now have our plan with all the content as json and can either export it and use it as backup or provide it as input for an import.
If the user decides to also import everything, he is again presented with the same workflow to get the target plan (it needs to pre-exist, the tool is not able to create new plans). The imports are POST and PATCH requests (see https://github.com/tfenster/PlannerExAndImport/blob/master/Planner.cs#L57) to the same endpoints with two important things to note:
With that you know how to ex- and import plans from Planner and my tool will easily allow you to do so. You will need to provide the name of the Azure tenant as well as the client id for the application. Then, if no previously cached token exists, the application will proceed with authorization or just reuse the existing token. After that you will go through the steps as outlined here.
A followup post will explain my build and release pipeline from GitHub repo to Docker image in the Docker hub but as there is a need to update that before sharing it, that might take a bit of time.