Export Work Item Information to Word Document

This is a series of posts on how to export data from Azure DevOps to a Word Document, composing word templates with Open XML Sdk.

The project is open source and available Here: https://github.com/alkampfergit/AzureDevopsWordPlayground

Post in the series:
1) API Connection
2) Retrieve Work Items Information
3) Azure DevOps API, Embed images into  HTML
4) Create Word Document For Work Items
5) Retrieve image in Work Item Description with TFS API
6) Retrieve Attachment in Azure DevOps with REST API in C#

Gian Maria

Error when .NET461 full framework references .NETStandard nuget packages

After updating MongoDb driver in a C# big project I start having a problem in a Web Project where we have this error after a deploy in test server (but we have no error in local machine of developers)

An assembly with the same identity ‘System.Runtime, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ has already been imported. Try removing one of the duplicate references.

This happens because MongoDB driver > 2.8 reference in the chain System.Buffer 4.4.0. If you take an empty .NET Full Framework 4.6.1 and references MongoDb 2.8.x driver you can verify that you have

image

Figure 1: System.Buffers reference for the project

This seems an innocuous reference, but if you look closely to what is inside System.Buffer you can verify that there is no package for full framework. Just check in the packages directory to verify that there is no .NET full framework package.

image

Figure 2: There is no full framework package inside System.Buffers

This imply that, when you build the project, in the bin directory you will find all the dll of netstandard project, because actually you are referencing netstandard 2.0 dll.

image

Figure 3: All netstandard dlls included in the bin directory of the project.

Now you have a problem, because you have netstandard runtime included in your project, most of the time it is safe, but there are situation where this can generate errors.

You can find the issue in GitHub #39362, that in turn is a duplicate of #39362 (my original issue).

Actually, if you want to avoid problems, you should be 100% sure not to reference any netstandard dll in a .NET 4.6.1 full framework project, or you should use full framework 4.7.2 or greater.

Gian Maria.

Unable to Sysprep Windows 10 due to Candy Crush …

I was trying to sysprep a Windows 10 virtual machine hosted in Hyper-V but I got error messages like

Package CandyCrush.. was installed for a user, but not provisioned …

It turns out that Win 10 standard installer installs some application from the store that conflicts with sysprep. Now I need to uninstall one by one and to speedup the process I suggest you to use Get-AppxPackage powershell commandlet.

As an example to uninstall every application called Candy you can issue

Get-AppxPackage –Allusers *Candy* | Remove-AppxPackage

After you uninstalled all unwanted application you should be able to sysprep your Widnows 10 machine.

Gian Maria.

Retrieve image in Work Item Description with TFS API

When you try to export content of Work Item from Azure DevOps (online or server) you need to deal with external images that are referenced in HTML fields of Work Item. I’ve dealt in the past on this subject, showing how you can retrieve images with Store and Attachment Work Item Property.

Sadly enough, I’ve encountered situation with on-premise version of TFS where I found this type of image src inside HTML fields.

https://nameoftfsserver/NameOfCollection/WorkItemTracking/v1.0/AttachFileHandler.ashx?FileNameGuid=AB6fc2e0-c449-4090-ab98-fac6c87fc219&FileName=temp1554203067610.png 

As you can see the image is stored inside TFS as attachment, because it is served by AttachFileHandler, but you do not find any information of this image in Work Item Attachments property.

Tfs  / Azure DevOps has different technique to attach images to Work Item Description and image file is not always a real Attachment of the Work Item

This format has no FileId to retrieve the image with the Store interface, nor is present in the Attachments collection of current Work Item, so we need to download it with a standard WebClient, instead of relying to some specific API call. The problem is authentication, because if you simply try to use a WebClient to download the previous url you will got a 401.

The solution is to populate the Credentials property of WebClient with the current credentials used to connect to the TFS, in my situation I’ve this value into an helper class called ConnectionManager.

image

Figure 1: How to correctly retrieve image attached to a work item.

The code is really simple, just get the credential from Connection manager, generate a temp file name and use WebClient to download image content.

TfsTeamProjectCollection class, once connected to TFS / Azure Devops instance, contains an instance of used credentials in Credentials property. This can be used to do standard request with WebClient to the server.

The GetCredentials() method of ConnectionManager is really simple because, after connection is stabilished, the instance of TfsTeamProjectCollection class has a Credentials property that contains credentials used to connect to the server.

Armed with correct credentials, we can use WebClient standard class to download images from the server.

Gian Maria

How to delete content in Azure DevOps wiki

Today I got a simple but interesting question about Azure DevOps, how can I completely delete the content of the wiki? There are not so many reason for this, but sometimes you really want to start from scratch. Now suppose you have your wiki:

image

Figure 1: Wiki with a simple page

You have created some pages, you played a little bit with the wiki, you attached some cute pets photo and content to the wiki itself, maybe just to gain familiarity with the wiki itself.

image

Figure 2: Wiki with some content on it.

Now you want to delete everything, such as that no member of the team should be able to retrieve pages and content anymore.

Azure DevOps Wiki are nothing more than a Git Repository with MarkDown content, so you can directly manipulate git repository if you need to alter wiki history

To do a low level manipulation of the wiki, you should simply clone wiki repository locally, you can simply find repository url in the UI

image

Figure 3: Clone wiki repository from the ui.

That menu option simply lets you to grab url of the repository, then you can simply clone the repository locally and verify all the commits done in the wiki. (I use command line but you can use any UI of you choiche)

image

Figure 4: Content of the wiki, a simple git repository

Now if you look a Figure 4 you can notice that the wiki is nothing more than a git repository with a commit for each modification you did to the wiki. Now, if you really want to reset everything and start wiki from scratch, you can simply issue a

git reset --hard SHA_OF_FIRST_COMMIT

Where SHA_OF_FIRST_COMMIT is the address of the very first commit, the one with the comment Initializing wiki, in my example 86ec4c9. After the command was executed your local wikiMaster branch point to the very first commit of the repository, an empty wiki.

image

Figure 5: Your local wikiMaster branch was reset to the very first commit, now wikiMaster point to an empty wiki

Now you can simply push with –force option to reset remote branch to the very same commit.

git push --force

Open again wiki page to verify that now it reverted to the original version. Actually the server still has the previous commit in the database, but they are not reachable anymore and they will be deleted over time by internal garbage collection.

Resetting to the very first commit actually delete everything from the wiki, restoring it to its pristine content

This scenario is not really common, but a real common scenario is when you mistakenly write something in the wiki, save the page and then you want to delete what you have written. There are lots of reason for this requirement, you mistakenly inserted sensitive data like passwords or tokens, or you simply write something that you want to permanently delete.

If you look to Figure 4, suppose you simply paste a wrong image and you want to remove that image and all related content from the history of the page. If you simply edit the wiki page, remove the image, then save again the page, the data is still in the history, anyone can find again the content you want to remove. The only solution is to rewrite git history.

Since a Wiki is a git repository, everything you did remain in history of the page, if you included sensitive information, even if you edit the page, removed that information and save again is not enough.

From Figure 4 you can verify that the incriminated commit is 97e520e. If you followed my previous example you can simply reset everything to the previous commit, actually deleteing every content that was inserted after that commit.

git reset --hard 97e520e^

Special char ^ indicates first parent of a commit, so previous instruction tell git to reset to the commit parent of bad commit. After this operation a git push – force will reset the branch from the server. The incriminated content is now gone, along with every content that was inserted after. Actually you restored wiki content to a past point in time.

Git reset –hard in your wiki repository allows you to restore a Wiki on a point in time, but everything that happened after that moment will be lost.

This is not a perfect approach, suppose you realize that someone stored a password in the wiki some days ago, you do not want to lose everything but simply remove that specific content and leaving other commit unchanged. Thanks to git flexibility you can obtain this operation with an interactive rebase.

git rebase 97e520e^ -i

This will actually trigger a complete rewrite of the history from the parent of the incriminated commit to the last commit of the wiki. I’m not going to give you a complete explanation of an interactive rebase, but basically you are presented with the list of all commits, starting with the commit you want to delete to the latest commit in the branch.

image

Figure 6: Delete the commit with interactive rebase.

In Figure 6 you are seeing an example in which I have a single commit after the one you want to remove, but nothing changes if you have tons of commits after. You simply need to change the command for the first commit (the commit you want to delete) from pick to d (delete). Leave all other rows unchanged. Then simply save the script to continue (if you are not familiar with VIM simply press I to edit the file, change the file then press ESC to come back in command mode and press : then w then q then ENTER).

This command actually deletes only the commit you want to delete, leaving all following commits unchanged. You actually scissor knife removed a single bad save from your wiki.

image

Figure 7: Commit was removed, local branch has not anymore commit 97e520e

Now you should be 100% sure that no one else modified the wiki in the short timespan you need to clone and rebase the repository so you can issue a git push –force to overwrite content of the repo on AzDo instance.

A git interactive rebase is an operation where you are rewriting history, so you can selectively remove a single commit from the history.

This will actually preserve all content of the wiki, you only removed a single commit from the wiki. There is no more history of that commit inside the Wiki. (actually deleted commit is still unreachable on the server, but there is no way for other to retrieve it).

If you want to completely remove a page with all the history of that page, you need to delete multiple commits, but luckily git has a filter-branch or more advanced comment. You can find more detail here https://help.github.com/en/articles/removing-sensitive-data-from-a-repository

Have I ever told you how much I love Git? :)

Gian Maria.