Start as a first time contributor

For me it all started at Collab Days in Belgium where I met Milan Holemans and Jasey Waegebaert, 2 great people who are active as a maintainer on the CLI for Microsoft 365. We talked a bit on this event and they told with great enthousiasm about what the CLI for Microsoft 365 is all about. The CLI is written in TypeScript and since I’ve been switching to more client side development for several years now, they had my attention with this one

The day after Collab days I contacted them, and they were able to explain to me with great enthusiasm how you can start as a contributor on the CLI for Microsoft 365.

Create your fork

First things first. Before you can start, you have to create your local fork.

A fork is a new repository that shares code and visibility settings with the original “upstream” repository

On this fork you can change things to your heart’s content without breaking the upstream code. It is recommended that for each issue you do for the CLI for Microsoft 365, you create a separate branch. In this way, every issue you solve is protected in one separate branch.

What also important is, is that you have to pull the fork that you have just made locally. You do this by taking the link from your github fork and cloning it locally with the command “git clone”, followed by the url of your fork.

Then, with your command prompt, go to the folder where you cloned your fork and run the command “npm install” to install the dependencies needed that the CLI for Microsoft 365 uses.
Of course you need Node.JS minimum version 16 and node package manager minimum version 8.

Then run the command “npm build“, to build te code. And finally, you run the command “npm link” so that you create a reference to your local project.

Be careful because if you had already installed the CLI for Microsoft 365 globally, you must first uninstall it before you run “npm link”. Once these commands have been executed, you can use your local version when using m365 in your shell.

   git clone *fork url*
   npm install
   npm build
   npm link

Where to start?

If you are just starting out as a contributor for the CLI for Microsoft 365, it is best to look at the issues labeled “Good First issue”. These issues are a little easier to start with. You can create an overview of only the issues with this label by clicking on the label somewhere,

or using the filter under “Label”,

or you can click this link.

Afterwards you can scroll through the list and see which ones interest you the most. If you want to claim an issue yourself to solve, it is important that this issue is not already signed to someone, you can see this if there is an icon in the column “Assignee” next to the issue.

If you have chosen an issue that interests you, you can open it and leave a comment field in which you indicate that you would like to claim that issue and ask to assign you. Shortly after you leave this comment on an issue, a maintainer will assign you.

This will be done by one of the 10 maintainers who are willing to deliver a better version of the CLI for Microsoft 365 every day. I had the honor of working with these individuals for several months and I have to say, they are all very nice personalities to work with. This is one of the reasons why I chose the CLI for Microsoft 365 open source project, this open source project is very lively in my opinion and you get so much appreciation for what you do.

Once the first command was done and I got my first PR approved it did trigger a sort of addicting spark. So, I ended up signing on for another one immediately.

Albert-Jan Schot –

Types of issues

There are generally 4 types of issues that are created:

  • New Command: usually proposed by one of the maintainers. A new command is always quite straight forward because it is always discussed with the whole community before a new command is opened to be solved. In addition, a new command lists the different options that belong to the command and explains what the command has to do. And sometimes it may be that an API is already documented in the issue that you can then use to display the desired result in the output of the command.
  • Enhancement: an existing command that must get a certain update. This can be a new option that needs to be added or an existing option that needs to be removed or replaced.
  • Bug: mostly discoverd by someone that reports this as an issue.
  • Script: A PowerShell script to archieve a certain goal.

How to start a new command

The first thing you need to do is go to the location where the command should be in the repository, these are grouped according to the verb and then alphabetically. When you have found the right location, you create 2 files in that folder. A .ts file for the actual command and a spec.ts file for the unit tests.

The .ts file

Below you will find an example of what a minimal .ts file looks like for a command. Where inline comments explain what each part is for

  // all the imports that the command uses
  import { Logger } from '../../../../cli/Logger';
  import GlobalOptions from '../../../../GlobalOptions';
  import PowerPlatformCommand from '../../../base/PowerPlatformCommand';
  import commands from '../../commands';
  import { validation } from '../../../../utils/validation';
  interface CommandArgs {
    options: Options;
  // all the options the command uses, options with the '?' are optional
  export interface Options extends GlobalOptions {
    environment: string;
    id?: string;
    name?: string;
    flag?: boolean;
  // The classname of the command, is a concatenation of all parts that the command uses. As example, this command is 'm365 pp card get', so the name of the class becomes a concatenation of pp, card & get
  class PpCardGetCommand extends PowerPlatformCommand {
    // the actual name of the command, gets its info from the commands file, so the command knows it is being called when 'm365 pp card get' is executed
    public get name(): string {
      return commands.CARD_GET;
    public get description(): string {
      return 'the description for the command';
    // the default properties that are used when the output 'text' or 'csv' is used
    public defaultProperties(): string[] | undefined {
      return ['name', 'cardid'];
    // the constructor
    constructor() {
    // This function will collect and measure how many times an optional option is used. The mandatory options should not be added to this
    #initTelemetry(): void {
      this.telemetry.push((args: CommandArgs) => {
        Object.assign(this.telemetryProperties, {
          id: typeof !== 'undefined',
          name: typeof !== 'undefined',
          flag: !!args.options.flag
    // This function defines the options associated with the command. Use '' for mandatory options, '[]' for optional options and when you just use '--option', it defines as a flag
    #initOptions(): void {
          option: '-e, --environment '
          option: '-i, --id [id]'
          option: '-n, --name [name]'
          option: '--flag'
    // this defines option sets. For example, if you want either id, or name is used as an option for the command but not both at the same time
    #initOptionSets(): void {
        { options: ['id', 'name'] }
    // if certain options have to be validated before the command can be executed. For example, an ID that needs to be validated to see if it's a valid guid
    #initValidators(): void {
        async (args: CommandArgs) => {
          if ( && !validation.isValidGuid( as string)) {
            return `${} is not a valid GUID`;
          return true;
    // the actual command where all the magic happens
    public async commandAction(logger: Logger, args: CommandArgs): Promise<void> {
  module.exports = new PpCardGetCommand();

The .spec file

The spec.ts file contains the unit tests for the command. It is important that these tests must always ensure 100% code coverage since the CLI for Microsoft is satisfied with nothing less. Writing these tests was new to me and created a learning curve. But I have to say that I have benefited a lot from Martin Linstuyls blog Where he explains in a clear way how to best use and write these tests


Last but not least: Documentation. Every command has it’s documentation.

This is used to have a clear documentation database on the CLI for Microsoft 365 website. The documentation is housed in the “docs” folder of the repository, also arranged according to their verb and then alphabetically.

It is also important that this documentation is included in the table of contents and you do this by adding the path to the mkdocs.yml file.

Publish it!

Once you’re done and you’ve checked everything again, you can commit and publish your branch to your fork in github. Then you go to your github fork and create a new pull request. By creating a pull request you indicate that you want to merge certain pieces of code with the main code of the CLI. Of course, you cannot do this directly, but via a pull request you allow the maintainers to do this for you. In the title of the pull request you give a short description. For example “adds command…” or “enhances command…”, followed by the command and closed with a railway sign followed by the 4 digits of the issue.

You do the same in the description field with a slightly longer explanation, added with any remarks if there are any. But in the description too you have to write a railway sign with the 4 digits of the issue. This allows the PR to be linked to the issue.

Once the PR has been created, you wait until one of the maintainers feels called to check your PR. When checking your PR, they will check whether or not there are improvements or errors in it. If this is the case, they will request changes to your PR and convert your PR In draft modus. You can then make these changes, mark every change as resolved and when you are done, you can mark your PR as “ready for review”. Once the maintainers find that your PR is complete, they will approve it and merge it with the main code of the CLI shortly afterwards.

This blog post is a written version of the session given during the Microsoft Viva Connections & SharePoint Framework Community call. The slides from this session can be found here.

Blog at

%d bloggers like this: