M365 Configuration as Code - Technical Guidance
Who is this for?
This page is intended for staff who are responsible for the configuration, security and operation of the Ministry of Justice’s Microsoft 365 services.
It holds information on the Ministry of Justice’s Microsoft 365 DSC DevOps integrated architecture. including detailed guidance, engineering references, and an overview of the intended scope and configuration. The aim is to facilitate the onboarding process and support administrators managing our tenants.
What is Microsoft 365 DSC?
Microsoft365DSC is an Open-Source initiative lead by Microsoft engineers and maintained by the community. It allows you to write a definition for how your Microsoft 365 tenant should be configured, automate the deployment of that configuration and ensures the monitoring of the defined configuration, notifying and acting on detected configuration drifts.
It is the primary tool used by the MoJ to automate the management of the organisations tenants in line with the NCSC & Microsoft blueprint for UK Government.
Microsoft365DSC is built as a module for the PowerShell Desired State Configuration framework and is made available via the PowerShell Gallery.
Architecture Overview
This solution provides a comprehensive framework for automating the management of Microsoft 365 configurations using Microsoft365DSC and Azure DevOps. It is composed of two Git repositories:
Staff-Infrastructure-Microsoft365-DSC-Configuration
Staff-Infrastructure-Microsoft365-DSC-Infrastructure
These repositories work together to enable configuration management, compliance testing, and deployment orchestration for the MoJ’s Microsoft 365 environments.
Components
- Azure DevOps Pipelines - Azure Pipelines are an Azure DevOps service for continuous integration and continuous delivery (CI/CD). Azure Pipelines are used to implement quality gates and ensure that changes are deployed in a controlled and consistent manner.
- Managed DevOps Pool - Managed DevOps Pools in Azure DevOps provide a fully managed service where virtual machines or containers powering the agent pools used to run the pipelines, offering seamless scalability and management for CI/CD pipelines.
- NAT Gateway - Azure NAT Gateway is a fully managed and highly resilient Network Address Translation (NAT) service that enables instances in a private subnet to connect outbound to the internet while remaining fully private, ensuring secure and scalable outbound connectivity.
- Key Vault - Key Vault improves the security of storage for tokens, passwords, certificates, API keys, and other secrets. It also provides tightly controlled access to the service principles certificates that are used in this solution.
- Storage Account - Azure Storage Accounts provide scalable and secure storage solutions for various data types, including blobs, files, queues, and tables, and are used in this M365 DevOps framework to store and manage modules, ensuring efficient and reliable deployment and configuration of Microsoft 365 environments.
- Microsoft365DSC - provides automation for the deployment, configuration, and monitoring of Microsoft 365 tenants via PowerShell Desired Stage Configuration (DSC). Microsoft365DSC is used to deploy configuration changes to the Microsoft 365 tenants via Azure Pipelines.
- Windows PowerShell DSC - is a management platform in PowerShell. You can use it to manage your development infrastructure by using a configuration-as-code model. This model is the underlying technology that Microsoft365DSC uses.
- GitHub - GitHub is the leading platform used for version control and collaboration of code within the MoJ, enabling developers to manage and share their code, track changes, and collaborate on projects seamlessly.
Well-Architected Framework Alignment
This section details how our solution aligns with the Microsoft Well-Architected Framework alongside CAF & NCSC guidance, ensuring a robust, secure, and efficient architecture. By adhering to the five core pillars of the framework—Reliability, Security, Cost Optimization, Operational Excellence, and Performance Efficiency—we have designed a solution that meets the standards set out in the framework. Our approach includes implementing stringent security measures to protect data integrity and confidentiality, optimising costs through efficient resource management, and ensuring operational excellence with comprehensive monitoring and automated systems.
Reliability
Deploying a Managed DevOps Pool to run our pipelines on significantly enhances reliability. Managed DevOps Pools provide a fully managed service where virtual machines powering the agents rely on Microsofts infrastructure for reliability ensuring that availability and fault tolerance is managed by Microsoft therefore reducing the risk of downtime and improving overall system stability. The managed nature of these pools means that updates, scaling, and maintenance are handled automatically, ensuring consistent performance and minimising disruption
Security
Using the combination of Managed DevOps Pools, NAT Gateway, and Private Endpoints for Key Vault and Storage Account access significantly enhances the security of our solution, aligning with the Security pillar of the Microsoft Well-Architected Framework. Managed DevOps Pools ensure that the infrastructure is maintained by Microsoft, reducing vulnerabilities and ensuring compliance with security best practices. The NAT Gateway provides secure outbound internet connectivity for resources in private subnets with a dedicated public IP address to be used within conditional access on the managed tenants, while preventing unsolicited inbound connections, adhering to the zero trust network security model. Additionally, Private Endpoints for Key Vault and Storage Accounts enable secure, private access to these resources by using private IP addresses within the virtual network, eliminating exposure to the public internet. This setup ensures that sensitive data remains protected and access is tightly controlled.
Cost Optimization
Due to the small footprint of resources required for this solution it natively comes with a low total cost of ownership, this has been further supplemented by the auto scaling features of Managed DevOps Pools to ensure there is no requirement for standing disk and compute.
Operational Excellence
Separate GitHub repositories and Azure DevOps projects are used to segregate the management of the tenant configurations from the CICD infrastructure to ensure that access to resources are limited to the teams who are responsible for parts of the service. Furthermore the DSC configuration leverages the use of tenant specific data files combined with shared baseline configurations to promote modularity, reduces redundancy, and improves maintainability.
The use of the CODEOWNERS file within the configuration repository allows GitHub teams to be set as the owners of specific files to be used within approval workflows to ensure that any changes made to configurations are approved by the technical authority of the product team who are responsible for the given M365 service.
This approach aligns with industry best practices for managing DSC configurations, ensuring modularity, security, and maintainability. It also supports the Well-Architected Frameworks principles of operational excellence and security by providing a structured and secure approach to configuration management
Performance Efficiency
This architecture makes use of a storage account to cache the Microsoft 365 DSC modules and its prerequisites to make them readily available to the agent pool upon each pipeline run. When a new version of Microsoft 365 DSC is specified in the CICD repository, a pipeline will automatically make sure the prerequisites are downloaded, packaged and uploaded to the specified Blob Storage, this improves the performance of each pipeline run by decreasing the agent preparation time therefore improving the performance efficiency of the solution.
Pipelines
The Pipelines directory in the Configuration repository contains Azure DevOps pipeline definitions:
build.yaml
: Builds MOF files from configuration data.dependencies.yaml
: Manages dependencies and secrets for tenants.deployment.yaml
: Deploys configurations to Microsoft 365 tenants.export.yaml
: Exports tenant configurations for auditing.prvalidation.yaml
: Validates pull requests for configuration changes.testcompliancy.yaml
: Tests tenant compliance with desired configurations.
These pipelines extend templates from the Infrastructure repository to execute the orchestration logic.
Scripts
The Scripts directory in the Infrastructure repository contains PowerShell scripts for orchestration:
Build.ps1
: Compiles MOF files from configuration data.CheckDscCompliance.ps1
: Tests tenant compliance with desired configurations and generates reports.Deploy.ps1
: Deploys configurations to Microsoft 365 tenants.Export.ps1
: Exports tenant configurations for auditing.SupportFunctions.psm1
: Contains helper functions used across scripts.
Getting Started
Initial Setup Guide
Learning Material
Responsibility Matrix
Getting Help
The CICD infrastructure that delivers the configuration to our M365 tenants is owned by the EUCS automation platform team, if you wish to raise a bug or make a feature request with the service please contact them using their shared team mailbox , or create a GitHub issue on the CICD repository.
If you require help with, or want to discuss configuration changes for a specific M365 product that is being managed by Microsoft 365 DSC please reach out to the relevant team as per the below table:
M365 Service | Product Team |
---|---|
Org Settings | MODERN WORKPLACE |
Entra | IDAM |
AzureDevOps | EUCS AUTOMATION PLATFORM |
Defender | Please speak with the product team relevant to where the Defender policy applies |
Exchange | MODERN WORKPLACE |
Fabric | APPLICATION PLATFORM |
Generic | EUCS AUTOMATION PLATFORM |
Intune | APPLICATIONS DEVICES |
Office365 | MODERN WORKPLACE |
OneDrive | FILE AND DATA |
Planner | MODERN WORKPLACE |
PowerPlatform | APPLICATION PLATFORM |
SecurityCompliance | MODERN WORKPLACE |
SharePoint | FILE AND DATA |
Teams | MODERN WORKPLACE |
Managing Configuration
The following section details how the workload configuration is set between the various data files within the Configuration repository.
What should be managed in Microsoft 365 DSC?
The intended scope of management in Microsoft 365 DSC should focus on persistent, core configuration settings that are crucial for maintaining the desired state and security posture of the tenants. This includes global settings such as isSingleInstance = $true, as well as workload settings such as conditional access policies, MPIP labels & EXO organisation config.
Microsoft 365 DSC is not intended for managing disposable or high volume configurations, such as user management or business driven security groups, which are more transient and subject to frequent changes. By concentrating on stable, foundational settings, Microsoft 365 DSC ensures a consistent and secure configuration baseline across the organisation.
Workflow
- Admin creates a new branch from the
main
Staff-Infrastructure-Microsoft365-DSC-Configuration repository, with a name relevant to the change they are making e.g.DEV-TeamsMeetingPolicy-update
- Admin makes the required configuration changes to the data files necessary to the change they are making
- Admin commits and syncs the changes to the feature branch
- Admin creates a pull request (PR) to merge the changes to the
main
repository, proving a description of what changes have been made to allow others to more efficiently peer review the work - The build pipeline runs on the PR
- Depending on the files that have been changed the relevant lead engineers or architects will be asked to review and approve the change, this is defined based on the content of the
codeowners
file - The merged PR triggers a pipeline to compile Managed Object Format (MOF) files. The pipeline calls Azure Key Vault to retrieve the credentials that are used by the tenant specific service principle(s) in the MOF files, and publishes the artifacts ready for deployment
- The deployment pipeline is triggered that uses the compiled MOF files to deploy configuration changes to the tenants that are managed via Microsoft365DSC
- Admin who committed the changes reviews the relevant pipeline & config in the M365 admin center to confirm the expected changes have been successfully applied
Data Files
A data file in PowerShell Desired State Configuration is a structured file that contains configuration data, such as node-specific settings and environment variables, which are used to customise and manage the desired state of target nodes (in this instance, tenants)
When reviewing the Configuration repository, you will see a folder called datafiles
. This folder contains two child folders, one named Tenants
and another called templates
The Tenants
folder contains all of the tenant specific configuration files, the templates
folder contains all of the global configuration files that are to be applied to all tenants under the management of Microsoft 365 DSC.
Global Settings
Global
The global layer data files define the workload settings that will be applied to all tenants that are under the management of Microsoft 365 DSC as the base configuration.
Location: Staff-Infrastructure-Microsoft365-DSC-Configuration\DataFiles\Templates\Global\Global*.psd1
Enforced
The enforced data files define settings that are enforced for all tenants. Settings in this file must be present in the global files with the same values and cannot be specified in any of the tenants specific files.
These settings are validated using a to a unit test approach during the build pipeline, this is first validated against the global layer, requiring all settings that are present in the enforced data files to be configured exactly as specified in the related Global datafiles. If this test is successful a second unit test is carried out against the tenant layer checking that none of the settings are present within tenant specific files, preventing all settings in the Enforced layer from being overridden with different values.
Location: Staff-Infrastructure-Microsoft365-DSC-Configuration\DataFiles\Templates\Enforced\Enforced*.psd1
Tenant Specific Settings
Tenant
These are the settings that are tenant specific. Either because the workload setting is specific to only that tenant so cannot be present in the global configuration, or because you want to override a setting from the Global layer (SettingX = 1 in Global, but for this tenant it should be SettingX = 2)
Location: Staff-Infrastructure-Microsoft365-DSC-Configuration\DataFiles\Tenants\<Tenant>\<TenantName><WorkloadName>.psd1
Example Configuration
The following section is intended to visualise how settings would be applied to DEV, NLE & PROD based on how various workload settings were configured within different data files.
In this example, the Global
configuration contains the following settings:
@{
NonNodeData = @{
Teams = @{
ChannelsPolicies = @(
@{
AllowOrgWideTeamCreation = $false
AllowChannelSharingToExternalUser = $true
}
)
}
}
}
In this example, the Enforced
configuration contains the following settings:
@{
NonNodeData = @{
Teams = @{
ChannelsPolicies = @(
@{
AllowChannelSharingToExternalUser = $true
}
)
}
}
}
In this example, the DEV
tenant specific configuration contains the following settings:
@{
NonNodeData = @{
Teams = @{
ChannelsPolicies = @(
@{
AllowOrgWideTeamCreation = $true
}
)
}
AzureAD = @{
TenantDetails = @(
@{
TenantId = DEV
}
)
}
}
}
In this example, the NLE
tenant specific configuration contains the following settings:
@{
NonNodeData = @{
Teams = @{
ChannelsPolicies = @(
@{
AllowOrgWideTeamCreation = $true
}
)
}
AzureAD = @{
TenantDetails = @(
@{
TenantId = NLE
}
)
}
}
}
In this example, the PROD
tenant specific configuration contains the following settings:
@{
NonNodeData = @{
AzureAD = @{
TenantDetails = @(
@{
TenantId = PROD
}
)
}
}
}
When the build pipeline runs, it will read and merge the various datafiles contained within the Configuration repository using the logic displayed within the diagram shown below, resulting in applied settings within each tenants MOF file shown at the bottom of the flow.
flowchart TD A[Build Pipeline start] --> B[Read and merge Enforced configuration files] B --> C[Read and merge Global configuration files] C --> D{Mandatory settings present in Global?} D -->|PASS| E[Read and merge Tenant-specific configuration files] D -->|FAIL| F[Error: Enforced settings missing in Global] E --> G{Enforced settings absent in Tenant?} G -->|PASS| H[Merge Global and Tenant configurations] G -->|FAIL| J[Error: Enforced settings present in Tenant] H --> I[Build script completed successfully] H --> J1[Applied Settings in MOF files] J1 --> K[DEV: AllowChannelSharingToExternalUser = true, AllowOrgWideTeamCreation = true, TenantId = DEV] J1 --> L[NLE: AllowChannelSharingToExternalUser = true, AllowOrgWideTeamCreation = true, TenantId = NLE] J1 --> M[PROD: AllowChannelSharingToExternalUser = true, AllowOrgWideTeamCreation = false, TenantId = PROD]
Understanding Available Properties
To understand the available properties and settings for a given version of Microsoft365DSC, you can visit the M365DSC Resources Overview, this page details the parameters that are available for configuration within each workload, including examples and the Graph API permissions required for the workload resource to be managed.
It is important to note that due to the setup of the data files used within the solution you do not need to specify the workload within each resource that you are managing e.g. AADConditionalAccessPolicy would become ConditionalAccessPolicy within the data file, as the workload e.g. AzureAD will be defined at the top of the datafile and be prepended to all resources within that file.
You can also use the M365DSC.CompositeResources module. This module provides a complete reference of all resources and settings.
To export an example data file to a location of your choice, follow these steps: 1. Install the M365DSC.CompositeResources module:
Install-Module -Name M365DSC.CompositeResources -Force
- Run the following command to generate an example data file:
New-M365DSCExampleDataFile -OutputPath "C:\Path\To\Output"
This will create a sample configuration file with all available properties.
For more complex workload configurations, where it is challenging to work out the required parameters it is advised to set up the required configuration within the DEV tenant via ClickOps, and then export the config and upload the required code into the datafiles to ensure accuracy.
Exporting Existing Configurations
You can export the existing configuration from a tenant using the Export Pipeline. This pipeline produces an artifact containing both the raw DSC output and the composite module (where available) for the workloads defined in the tenants generic file.
- Pipeline:
Staff-Infrastructure-Microsoft365-DSC-Configuration\Pipelines\export.yaml
- Script:
Staff-Infrastructure-Microsoft365-DSC-Infrastructure\Scripts\Export.ps1
Onboarding a new tenant
To onboard a new M365 tenant to be managed by this solution please see the below implementation guidance.
Governance
This page provides a comprehensive guide to managing code ownership, branch protection, and recording architectural decision records for workloads in the Microsoft365DSC repositories. It explains how to configure the CODEOWNERS file, enforce branch protection rules, and establish workflows for workload and platform administrators.
Architecture Decision Records
During our work managing M365 configuration, we will have to make architectural decisions about the platform and associated workloads.
When making decisions, we should record them somewhere for future reference, to help us remember why we made them, and to help teams working in related areas understand why we made them. To do this we will use We will use Architecture Decision Records, as described by Michael Nygard in this article.
Each of the CICD and Data GitHub repositories contains a /docs/architecture-decision-records folder, any design decisions relating to configuration of the Microsoft365 DSC solution, or M365 workloads should be recorded here e.g. “authentication-method-policy-sms-is-disabled” the ADR record should be stored within the folder relating to the tenant the decision applies too, if a decision is a global decision that is applied to all tenants under the management of M365DSC then it should be stored in the root ADR folder.
Code Ownership
The CODEOWNERS file in GitHub is used to assign responsibility for specific files or directories to teams. This ensures that the appropriate teams are automatically requested as reviewers for pull requests (PRs) affecting their areas of responsibility.
Key Points
The platform admin team are assigned ownership of:
* All generic.psd1 files
* All pipeline files
This ensures that platform administrators manage the core files and CI/CD pipelines.
The workload product team e.g. Modern Workplace Admins Team are assigned ownership of:
* Global settings: DataFiles/Templates/Global/*
* Enforced settings: DataFiles/Templates/Enforced/*
* Tenant-specific settings: DataFiles/Tenants/*
This ensures that workload administrators manage configurations for their respective workloads across all layers.
All other files are assigned to the eucs-automation-platform-admins team by default: *.
Branch Protection Rules
Branch protection rules enforce policies on protected branches (e.g., main, develop) to ensure that changes are reviewed, validated, and approved before merging.
The branch protection policies that are applied to the M365DSC respositiories are in line with the MoJ GitHub Standards XXXXXXXXX XXXXXXXXX XXXXXXXXX
Summary
Workload Admins:
- Contribute to workload-specific files (e.g., DataFiles/Templates/Global/, DataFiles/Templates/Enforced/, DataFiles/Tenants/*).
- Must use the feature branch and PR workflow.
- Cannot directly push to protected branches.
Platform Admins:
- Contribute to core files (e.g., */#Generic.psd1, Pipelines/*).
- Use the feature branch and PR workflow for regular changes.
- Have direct push access to protected branches for hotfixes or emergencies.
Branch Protection Rules:
- Enforce PR-based workflows for all changes.
- Require reviews from code owners and passing CI/CD checks.
- Restrict direct push access to eucs-automation-platform-admins.
Compliancy Checks
This solution runs the Check Compliancy pipeline twice daily, once at 08:00 and once at 18:00 daily. When this pipeline runs it checks the configuration of each tenant that are under management of Microsoft 365 DSC against the data files that are specified in the configuration repositories to make sure all workload settings that are defined within DSC are compliant. At current maturity this solution does not enforce automatic remediation actions if DSC notices a configuration drift within the tenant, the results of these checks are sent to XXXXXXXXX for administrators to observe.
By continuously monitoring the environment, MoJ ensure that the tenants are in their intended state before the start of, and at the end of, the organisations core working hours reducing the risk of misconfigurations that may impact the end user experience or security posture.
Breaking Changes
What are breaking changes?
Microsoft 365 is under constant development, which means that functionalities are being added, deprecated or removed all the time. Microsoft365DSC has to adapt to these changes and at the same time make sure existing users and configurations are impacted as little as possible.
In the cases where a new resource is added, a resource gets a new optional parameter or a parameter that used to be mandatory becomes optional, existing configurations are not impacted. But when a resource or parameter is removed or an optional parameter becomes mandatory, existing configurations can stop functioning, because they are using these removed components. That is what is called a “Breaking Change”: A change that can break existing configurations and therefore impact the administration process of Microsoft 365.
Twice a year Microsoft release a version that bundles all breaking changes of the previous six months into a single version, these releases occur in April and October annually.
How we handle the breaking changes release schedule
The release notes of the breaking changes versions will have all breaking changes clearly documented, including the required actions to take, which are almost certain to be required.
MoJ will lag the release of the breaking changes release by 8 weeks, to give a chance for the community to identify any issues or bugs with the latest release before carrying out a version uplift to the estate.
The version of Microsoft 365 DSC this solution is deployed with is managed by the application platform team as primary owners of the CICD configuration. Application platform will lead on the discovery and release of the breaking changes release to ensure that all product teams who manage their workloads via Microsoft 365 DSC are working together to evaluate the release and identify what changes are required to our configuration.
Reference Material
Microsoft 365 DSC GitHub Repository
Microsoft 365 DSCTools GitHub Repository
Microsoft 365 DSCCompositeResources GitHub Repository
Adding to this page
If there’s an update required, or something missing you believe others may find helpful, please either let us know and we’ll add a new article, or if you’re comfortable writing one yourself, PRs will be gratefully received. Details on how to get in touch are in the “Getting Help” section above, and the “GitHub” link at the top right of this page will take you to the repository for this guide.