SonarQube Tutorial – Part III: SonarScanner for MSBuild

During this tutorial, I assume that you have finished the SonarScanner tutorial and you have your SonarQube server, sonar scanner and example projects set and ready to play with. If not please check the previous tutorials for instructions!

Keep in mind this article is part of our series on SonarQube!

  1. SonarQube tutorial – how to get started?
  2. SonarScanner tutorial
  3. SonarScanner for MSBuild tutorial (you’re here!)
  4. Rules, quality profiles and quality gates
  5. Gitlab integration tutorial

The main differences between SonarScanner and SonarScanner for MSBuild

The biggest difference is the way of executing MSBuild scanner – you need to build a project. In order to make it work you need to explicitly tell the scanner to begin “listening” for the project building and after the build is finished you need to inform it about it being finished. A concept example is presented below together with a link to documentation.

SonarScanner.MSBuild.exe begin /k:"project-key"

MSBuild.exe <path to solution.sln> /t:Rebuild

SonarScanner.MSBuild.exe end

Analyzing with SonarQube Scanner for MSBuild official documentation


First MSBuild SonarQube analysis

  1. Run git clone
  2. Run cd eShopOnWeb
  3. Create Dockerfile
  4. Open created Dockerfile and paste the code below
FROM sonar-scanner-image:latest AS sonarqube_scan


COPY . .

# We are restoring package here in order to make logs clearer for us to read in sonar-scanner
# analysis execution section
RUN dotnet restore

RUN ls -list

# First difference between this scanner and the previous one is a way that we execute it.
# We need to execute 3 commands in order to trigger an analysis. Firstly we need to begin Sonarqube
# analysis and pass analysis parameters. Then we need to build an application and then finally we need to
# to explicitly end analysis.
# What is more syntax of passing parameters varies from the previous scanner. We have predefined special
# prefixes for project key, project name and project versions instead of passing them as casual
# parameter. Also instead of adding prefix "/D" for this scanner, we need to ad "/d:" instead.
# The last, but not least difference is that we need to authorize on both begin and end step
# if Sonarqube project is private.
# Except for the above differences other parameters is mostly working the same.
RUN dotnet sonarscanner begin \
/d:sonar.login="SONAR_LOGIN_TOKEN" \
/d:sonar.exclusions="**/wwwroot/**, **/obj/**, **/bin/**" \

RUN dotnet build --no-incremental

RUN dotnet sonarscanner end /d:sonar.login="SONAR_LOGIN_TOKEN"


  1. Remember to use the value of SONAR_LOGIN_TOKEN generated in previous tutorials.
  2. Run docker build --network=host --no-cache . to execute code analysis. You will be running this command multiple time during the next examples. Remember to execute them in the project root directory!
  3. Enter http://localhost:9000/dashboard?id=SONAR_NET_PROJECT_KEY to see analysis result


Add code coverage

Adding new SonarQube parameters in order to improve analysis is almost the same as in SonarScanner, but since we have 3 test projects in the example solution we can play with supporting that scenario.

We need to add a new package to our solution to generate code coverage reports:

  1. Run dotnet add tests\FunctionalTests\FunctionalTests.csproj package coverlet.msbuild --version 2.3.2
  2. Run dotnet add tests\IntegrationTests\IntegrationTests.csproj package coverlet.msbuild --version 2.3.2
  3. Run dotnet add tests\UnitTests\UnitTests.csproj package coverlet.msbuild --version 2.3.2

If you don’t have dotnet core on your computer then add the above command to dockerfile as RUN .. commands after COPY . .. In that case, remember to change \ to / in paths to make it work!

Add RUN dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover after RUN dotnet restore in dockerfile to have code coverage generated

Using SonarC# official documentation try to make code coverage working.



Show example solution (click here to open)
FROM sonar-scanner-image:latest AS sonarqube_scan


COPY . .

RUN dotnet restore
# Running tests with following parameters generate code coverage report
RUN dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover

RUN ls -list

# sonar.cs.opencover.reportsPaths - a similar case like for javascript code coverage parameter
# we need to pass paths to coverage reports.
RUN dotnet sonarscanner begin \
/d:sonar.login="SONAR_LOGIN_TOKEN" \
/d:sonar.exclusions="**/wwwroot/**, **/obj/**, **/bin/**" \
/d:sonar.links.homepage="CI_LINK_URL" \

# --no-incremental option marks the build as unsafe for incremental build. 
# This flag turns off incremental compilation and forces a clean rebuild of the project's dependency graph.
RUN dotnet build --no-incremental

RUN dotnet sonarscanner end /d:sonar.login="SONAR_LOGIN_TOKEN"



Add unit test results to analysis

Generating unit test results in XML format isn’t too straightforward with the current .NET core framework, so instead, we will generate it with the Visual studio tests format (.trx). Creating xunit test in XML format would require adding new packages like xunit.testLogger which makes no sense since we have test results with an out of the box solution.

In order to achieve it add --logger "trx;LogFileName=TestResults.trx" to command executing tests, so it will be looking like this: RUN dotnet test --logger "trx;LogFileName=TestResults.trx" /p:CollectCoverage=true /p:CoverletOutputFormat=opencover

Using SonarC# official documentation on Unit Test Execution Results Import try to add unit tests result to the analysis.

Show example solution (click here to open)
FROM sonar-scanner-image:latest AS sonarqube_scan


COPY . .

RUN dotnet restore
# Running tests with new option --logger presented below will generate test results in visual studio format
RUN dotnet test --logger "trx;LogFileName=TestResults.trx" /p:CollectCoverage=true /p:CoverletOutputFormat=opencover

RUN ls -list

# sonar.cs.vstest.reportsPaths - paths to visual studio test results format.
# In this example instead of providing the full path for all 3 reports I used pattern matching for it.
RUN dotnet sonarscanner begin \
/d:sonar.login="SONAR_LOGIN_TOKEN" \
/d:sonar.exclusions="**/wwwroot/**, **/obj/**, **/bin/**" \
/d:sonar.links.homepage="CI_LINK_URL" \
/d:sonar.cs.opencover.reportsPaths="tests/FunctionalTests/coverage.opencover.xml,tests/IntegrationTests/coverage.opencover.xml,tests/UnitTests/coverage.opencover.xml" \

RUN dotnet build --no-incremental

RUN dotnet sonarscanner end /d:sonar.login="SONAR_LOGIN_TOKEN"



Add an advanced rule ignoring for patterns

Currently, MSBuild scanner doesn’t support defining multicriteria rules on the scanner side, so we need to define them on the server side which is harder to maintain in my opinion. It can be defined in the general settings section under Analysis Scope section at the bottom of the page.

To get there enter this link http://localhost:9000/project/settings?category=exclusions&id=SONAR_NET_PROJECT_KEY.

Configuration is even simpler than it is for SonarScanner since we need to configure only the Rule Key Pattern and File Path Pattern. For example, the following screen shows a configuration for ignoring rule General exceptions and should never be thrown in all controllers.



Add issues raised by Roslyn analyzers

SonarQube analysis works out of the box with Roslyn analyzers as mentioned in the SonarQube documentation.

Let’s add a new analyzer to our project to test this feature. In order to do this, we need to add the following new package – ClrHeapAllocationAnalyzer.

  1. Run dotnet add src\ApplicationCore\ApplicationCore.csproj package ClrHeapAllocationAnalyzer --version
  2. Run dotnet add src\Infrastructure\Infrastructure.csproj package ClrHeapAllocationAnalyzer --version
  3. Run dotnet add src\Web\Web.csproj package ClrHeapAllocationAnalyzer --version


If you don’t have dotnet core on your computer then add the above command to dockerfile as RUN .. commands after COPY . .. In that case, remember to change \ to / in paths to make it work!

Run docker build --network=host --no-cache . to execute analysis and see results

scm issues


Add SCM information to SonarQube

In the SonarQube project dashboard you may have noticed a warning about no SCM information found.



Without it, SonarQube cannot automatically assign issues to developers who have written problematic code and cannot determine who’s code it is.

In order to fix this, we need to provide SCM information to SonarQube. We can do this in two ways. The first way is to set SCM configuration in SonarQube options – http://localhost:9000/project/settings?category=scm&id=SONAR_NET_PROJECT_KEY, which isn’t recommended.

The second way is to provide local SCM files during SonarQube analysis – in order to do this we need to modify .dockerignore file and remove .git entry.

After the next analysis, SonarQube should be able to use SCM data.


Keeping SonarQube MSBuild scanner configuration in the configuration file

Similar to SonarScanner we can keep configuration in  SonarQube.Analysis.xml file:

<?xml version="1.0" encoding="utf-8" ?>
<SonarQubeAnalysisProperties  xmlns:xsi="" xmlns:xsd="" xmlns="">
  <Property Name="sonar.exclusions">**/wwwroot/**, **/obj/**, **/bin/**</Property>
  <Property Name="sonar.verbose">true</Property>


We may keep the configuration file anywhere in the project, but we need to pass a path to configuration to command like this: /s:"$(pwd)/SonarQube.Analysis.xml". What is more we can’t define all properties this way – for example, we can’t define sonar.projectBaseDir.


Changing the leak period

The leak period is used to define what is a new code for our project. By default, the leak period is set to the project version, but I found out that in practice quite often depending on the project version is problematic and makes no sense. It may make sense to change it to a number of days, for example to a sprint length (in calendar days).

We can change it either globally as a default value or per project. To change it globally go to http://localhost:9000/admin/settings and at the bottom of the page you can change New Code Period property to a different value for example to 14 as in the screen below.

new code period

What next?

In the next tutorial, we will play a little with customization of server rules and behaviors in analysis context in Rules, quality profiles and quality gates tutorial.

We will wrap things up with the Gitlab integration tutorial, which will show us how to integrate SonarQube with pull requests.


Cleaning up after a tutorial

To stop a container with running SonarQube server instance run following command: (don’t do this if you want to continue with the next tutorials!)

docker container stop sonarqube

To remove also all docker containers run

docker container prune --force

Finally to remove all images used in this tutorial run

docker image remove sonarqube:7.5-community sonar-scanner-image

Is Code Quality important to you?
Our experts can take care of that!

Let's create
something meaningful together!

Company data
  • Setapp Sp. z o.o.
  • VAT ID: PL7781465185
  • REGON: 301183743
  • KRS: 0000334616
  • ul. Wojskowa 6
  • 60-792 Poznań, Poland
Contact us
Stay connected