Photo by Will Myers on Unsplash

Photo by Will Myers on Unsplash

Microsoft 365 DSC


Article from ADMIN 73/2023
The declarative PowerShell Desired State Configuration extension supports easy and transparent configuration of systems and applications. We describe the fairly complex initial setup and use of the Microsoft 365 Desired State Configuration.

In the Microsoft 365 environment, with its variety of configurations and multiple tenants to manage, Desired State Configuration (DSC) is an ideal way to track changes and reset a configuration to the desired state. Microsoft provides a great deal of information about the project [1], but in my experience, some of it is outdated. This article refers to the version available March 2022.

Preparations for DSC

Before setting up Microsoft 365 DSC (M365DSC), you need to create an Azure Active Directory (Azure AD) application that you will use later to authenticate the PowerShell script. Alternatively, you could log in with a username and password; I will look at the advantages and disadvantages of these methods in detail.

When authenticating with an Azure AD application, you use either a certificate you create yourself or a client secret. This example uses a certificate because it is the approach currently recommended by Microsoft. In the course of creating the Azure AD application, the certificate is stored in the certificate store of the currently logged in user – authentication only works for this user.

Working with SharePoint Online is essential in Microsoft 365, so most administrators use the PowerShell Office PnP module. Other approaches use the Azure AD web portal. However, I'll take the Office PnP route to create the Azure AD application. If you don't have the module installed, the following commands install it and grant the necessary rights to the PnP management shell in an administrative PowerShell:

Install-module PnP.PowerShell

Then, create the Azure AD application with:

Register-PnPAzureADApp -ApplicationName DSCAuthApp -Tenant <tenant name on> c:\DSC-CertificatePassword (ConvertTo-SecureString -String "<SecretPassword>" -AsPlainText -Force) -store CurrentUser -scopes "SPO.Sites.FullControl.All"-DeviceLogin

A certificate is automatically created in C:\DSC, and the password of the private key matches the SecretPassword parameter.

The certificate ends up in the user's certificate store and is uploaded to the Azure AD application. Therefore, the application is given full permissions in SharePoint (Sites.FullControl.All, Group.ReadWrite.All, and User.Read.All).

If multifactor authentication is enabled for the executing user, which should be a matter of course for an administrator in Microsoft 365, you also need to use the -Device Login parameter. Now a code is generated in PowerShell, which you then enter in the browser window that opens before logging in with the username and password. For more information, see the PowerShell PnP GitHub page [2]. After this preparation, install DSC with:

Install-Module Microsoft365DSC -Force

The first command after a successful installation should be:


Schedule some time here, because the command can take some time to complete.

Permissions need to be set in Microsoft Graph to match the components you want to access in Microsoft 365. If you want to use DSC for all components in Microsoft 365, the corresponding rights can be queried:

Get-M365DSCCompiledPermissionList -ResourceNameList (Get-M365DSCAllResources)

To use only individual components or simply check which ones exist, use the command:


All permissions for individual components, along with read permission for the SharedMailbox and TeamsUser components, are set by the cmdlets:

Update-M365DSCAllowedGraphScopes -ResourceNameList @("EXOSharedMailbox", "TeamsUser") -Type Read

The list of components is passed as an array in the ResourceNameList parameter; however, it is not possible to change (update) the components with the command.

This action requires active consent during execution. The commands

Update-M365DSCAllowedGraphScopes -All -Type Read
Update-M365DSCAllowedGraphScopes -All -Type Update

set the Read and Modify permissions for all components, if you so desire.

Login for Tenant Snapshot

The first step exported the configuration of a Microsoft 365 tenant and saved it as a text file (snapshot). Before continuing, the topic of authentication needs to be brought up again, because it's important to understand the risks involved.

As mentioned earlier, an application, including a PowerShell script, can authenticate to Azure AD, and therefore to Microsoft 365, in multiple ways. The two that are relevant here are logging in as a user account with a username and password or logging in as a service principal with an Azure AD application ID. You also have to distinguish between a login with a client secret and one with a certificate. Logging in with an Azure AD application ID and a certificate is always preferable to the username-password solution. On the one hand, this reduces the attack surface, and the maintenance overhead (disabled accounts, password changes) is lower, as well. Unfortunately, however, M365DSC does not let you log in to all components with an Azure AD application ID.

Although you can log in to Azure AD with an Azure AD application ID and a certificate, if you want to configure, for example, conditional access, you have to use a username and password. To take a snapshot of an entire tenant, your only approach is to log in with a username and password. You can export the data with


and either put together the command with its options at the command line or launch it with the -LaunchWebUI parameter. A redirection in the browser to lets you conveniently compose the export in a GUI, but note that not everything is selected by default (Figure 1). In Microsoft Planner task management software, for example, nothing is exported at all. If you want to include all data, select Full in the drop-down menu at top left and then press Generate at top right.

Figure 1: The -LaunchWebUI switch for Export-M365DSCConfiguration starts exporting a configuration in the GUI.

This action creates a script that is copied and then passed to PowerShell for execution. The process can take several minutes or even hours. You might also need to reauthorize the PnP Management Shell module, depending on the previous configuration and usage.

Once the configuration is complete, you have a snapshot of the tenant at the time of execution. Although that's all well and good, it's not really practical. An unsupervised export that runs, say, once a week would be preferable. This arrangement would require an interactive login, because the copied script always prompts for a username and password.

One alternative is to log in with the Azure AD application. If you do not want to export all components, but only those that are compatible with a login to an Azure AD application, this approach is best. You just need to test something. The script generated on the website requires ApplicationId, CertificateThumbprint, and TenantId to log in. The ApplicationId and TenantId identifiers can be found in the Azure AD application created previously under App Registration . If you go there and click on the app name, the header will give you the data. You will find CertificateThumbprint in Certificates & secrets in the app. I imported the private key into the certificate store earlier.

Because not everything works with an Azure AD application, you have no alternative to using a personal login. The challenge here is to encrypt the user password in the script, because the password can be changed to plain text in the script:

$password = "<Test1234>" | ConvertTo-SecureString -AsPlainText -Force

However, this solution is not ideal. It would be better to encrypt the password with an AES key. To do so, you need to store the password and the AES key in separate files and reference them in the script:

$AESKey = New-Object Byte[] 32
$AESKey | Out-File C:\DSC\<aes.key>
$password = Read-Host -prompt "<Please enter password>" -AsSecureString
$password | ConvertFrom-SecureString -Key $AESKey |Out-File C:\DSC\<password.txt>

The code

$username = "<Microsoft-365-Login>"
$AESKey = Get-Content C:\DSC\<aes.key>
$password = Get-Content C:\DSC\<password.txt> | ConvertTo-SecureString -Key $AESKey
$credentials = New-Object System.Management.Automation.PSCredential ("$username", $password)

handles the login in the script.

Exporting Configurations

The previously mentioned script from the website is exported and executed in PowerShell. Once it has completed, it prompts you for a storage location for the configuration. If you do not set anything here, the export file will always use the same name. To avoid this – or if you want to run the script periodically – add the command Export-M365DSCConfiguration with the -Path and -FileName parameters.

Alternatively, you could leave out the component list and use the -Mode parameter with the Lite|Default|Full arguments instead. A call that exports the entire tenant configuration and stores it in the C:\DSC path would be:

Export-M365DSCConfiguration -Mode 'Full' -Credential $Credentials -Path 'C:\DSC\'

Besides exporting, it is also possible and important to be able to import existing configurations again. To do this, the generated PS1 file must first be compiled into what is known as a managed object format (MOF) file by executing the M365TenantConfig.ps1 file in PowerShell.

However, the MOF file contains the password in plain text (Figure 2). Even if you used encryption or an encrypted user object previously, at this point it is always in plain text.

Figure 2: The MOF file contains the password as plain text by default.

Fortunately, you can use an on-board DSC tool to solve the problem:


The command generates a certificate that DSC uses to encrypt the credentials in the MOF file. Be careful: The operation will fail if you have not configured Windows remote management (WinRM) with the winrm quickconfig command. If the configuration is good, the encrypted password is displayed in the file.

Also important to note is that you must export a configuration after the certificate has been created. It makes most sense to run the command before the first export. When exporting, the corresponding certificate is then also stored in the folder and referenced accordingly in the ConfigurationData.psd1 file.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy ADMIN Magazine

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus
Subscribe to our ADMIN Newsletters
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs

Support Our Work

ADMIN content is made possible with support from readers like you. Please consider contributing when you've found an article to be beneficial.

Learn More”>


		<div class=