Microsoft 365 DSC
Outliers
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 Register-PnPManagementShellAccess
Then, create the Azure AD application with:
Register-PnPAzureADApp -ApplicationName DSCAuthApp -Tenant <tenant name on>.microsoft.com-OutPath 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:
Update-M365DSCDependencies
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:
Get-M365DSCAllResources
All permissions for individual components, along with read permission for the SharedMailbox
and TeamsUser
components, are set by the cmdlets:
Update-M365DSCAllowedGraphScopes 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
Export-M365DSCConfiguration
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 https://Export.Microsoft365DSC.com 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.
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 [Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($AESKey) $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.
Fortunately, you can use an on-board DSC tool to solve the problem:
Set-M365DSCAgentCertificateConfiguration
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
(incl. VAT)