Dec 20, 2020 3 min read

Use the CLI for Microsoft 365 Docker container in Azure DevOps

Use the CLI for Microsoft 365 Docker container in your Azure DevOps pipeline, with unattended/app-only authentication.

Use the CLI for Microsoft 365 Docker container in Azure DevOps

In line with my previous two blog posts (post 1, post 2), there is yet another way to run the CLI for Microsoft 365 commands in your Azure DevOps pipeline: a Docker container!

Azure DevOps pipelines theory

All steps in an Azure DevOps pipeline get executed on a build agent, which is in essence  a computer that provides a particular set of functionalities (like having PowerShell or node.js installed, etc.).
Things get a bit more complicated when you need to run actions with conflicting dependencies on a build agent.  The solution to this is either multiple build agents with their own set of dependencies installed, or running a Docker container for each set of dependencies and run your actions in the container.

Azure DevOps calls this a Container Job. Per usual, you still select a build agent but you also specify the container(s) from Docker Hub or other repositories (like Azure Container Registry) and they will be started on the agent.
You can still keep using all DevOps tasks and functionalities (like variables) in your pipeline, but they will now be executed in the container instead of directly on the build agent!

Very elegant, no?

There is a much better, much more detailed explanation of Container Jobs on docs.microsoft.com but this should be enough to understand the next part.

Using the CLI for Microsoft 365 Docker image in DevOps

CLI for Microsoft 365 is publishing images on Docker Hub, for each new release of CLI a new image will be made available automatically. It is a very easy and convenient way to get CLI and it's dependencies in your DevOps pipeline, to execute actions.

In my YAML definition I define the trigger, connect to the variable group and select a build agent on which everything will run:

trigger:
- master

variables:
- group: "Key Vault Secrets"

pool:
  vmImage: 'ubuntu-latest'

I define a job and configure it so that every step will be executed inside the Dcoker container provided by CLI for Microsoft 365:

jobs:
- job: RunInContainer
  container: 
    image: m365pnp/cli-microsoft365
    options: --user 0:0

  steps:

The special trick here is to provide --user 0:0 as options. When DevOps spins up the container, it does some magic tricks to make build variables and custom variables available inside it. It needs root access and this option will provide it.

Every subsequent step inside of this job will be executed in the container, instead of on the agent.

Inline script in bash

  - script: |
      m365 login --authType certificate --certificateBase64Encoded "$(YannicksDevOpsPipeline)" --appId e5c08ad8-xxxx-xxxx-b429-a266c94bc638 --tenant b868e8f4-xxxx-xxxx-8c95-7f432eba848b
      m365 spo web get --webUrl https://contoso.sharepoint.com/sites/crisismanagement
      m365 logout
    displayName: "Docker: Run M365 CLI in Bash"

Variables (both out-of-the-box as well as custom defined) can be used directly within the script itself.

Inline PowerShell script

  - task: PowerShell@2
    inputs:
      targetType: 'inline'
      script: |
        Write-Host "I'm PowerShell"
        m365 login --authType certificate --certificateBase64Encoded "$(YannicksDevOpsPipeline)" --appId e5c08ad8-xxxx-xxxx-b429-a266c94bc638 --tenant b868e8f4-xxxx-xxxx-8c95-7f432eba848b
        m365 spo web get --webUrl https://contoso.sharepoint.com/sites/crisismanagement
        m365 logout
      pwsh: true
    displayName: "Docker: Run M365 CLI in PowerShell, inline script"

Similar as in bash, the variables are available in here too.

External PowerShell script

  - task: PowerShell@2
    inputs:
      filePath: './m365cliscript.ps1'
      arguments: '-certificate "$(YannicksDevOpsPipeline)" -clientId e5c08ad8-xxxx-xxxx-b429-a266c94bc638 -tenantId b868e8f4-xxxx-xxxx-8c95-7f432eba848b'
      pwsh: true
    displayName: "Docker: Run M365 CLI in PowerShell, file path script"

With an externalized script, if you need pipeline variables in your script, you'll need to pass them as arguments.

Conclusion

Container jobs in Azure DevOps are very powerful, and with the Docker image provided by CLI for Microsoft 365 we have another great way to automate actions against our Microsoft 365 environment (+ it is about twice as fast in my tests as opposed to installing it on the hosted build agent).

Keep building, keep deploying!

Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to Yannick Reekmans.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.