//sbd.org.uk
Back to blog
Configuration as Code for Endpoint Management
·6 min read

Configuration as Code for Endpoint Management

Why clicking through consoles is technical debt, and how to escape without a Computer Science degree.

windowsintunegitopsdevops

The Problem with Console-Driven Management

Every change you make in the Intune portal is a liability. Not because the portal is bad—it's actually quite good—but because console clicks leave no meaningful trace of intent.

Yes, there's an audit log. It'll tell you that someone modified a configuration profile at 14:32 on Tuesday. What it won't tell you is why, what the previous value was, whether the change was tested, or whether it was even deliberate. When something breaks three weeks later and you're hunting through audit logs trying to correlate symptoms with changes, you'll understand the problem viscerally.

This is how configuration drift starts. A well-intentioned engineer tweaks a setting to fix an urgent issue. Another engineer, unaware, makes a conflicting change in a different profile. A third copies a profile for a new use case and modifies it slightly. Six months later, you have seventeen "Standard Device Configuration" profiles, none of which match, and nobody can articulate what "standard" actually means anymore.

Configuration as Code solves this by making your endpoint configuration:

  • Visible: The configuration exists as files you can read, not just as state in a database somewhere in Microsoft's infrastructure.
  • Versioned: Every change is tracked with who, when, and—critically—why (in the commit message).
  • Reviewable: Changes go through pull requests. A second pair of eyes catches mistakes before they hit production.
  • Repeatable: Spinning up a test tenant, recovering from disaster, or onboarding a new team member becomes trivial.
  • Reversible: Something breaks? Revert the commit, run the pipeline, and you're back to the last known good state.
Before: Console-DrivenENGINEER AENGINEER BENGINEER CIntune Portal(click, click, click)Production Tenant(unknown state)??!NO HISTORY · NO REVIEW · NO ROLLBACKAfter: Configuration as CodeENGINEERSGit RepositoryPR ReviewCI/CD PipelineProd Tenant(known state)VERSIONEDREVIEWEDVALIDATEDREVERSIBLEDRIFT DETECTION
Console-driven management creates ungoverned change; configuration as code enforces a reviewable, reversible pipeline

"We're Not Developers"

This is the objection I hear most frequently, and it's worth addressing head-on.

Configuration as Code doesn't require you to become developers. You're not writing applications. You're not learning algorithms or data structures. You're learning to use tools that happen to come from the development world because those tools solve problems that ops teams have been suffering with for decades.

The core skills are:

SkillWhat It Actually MeansLearning Curve
PowerShell basicsYou probably already have this. If you've written a script that queries AD or modifies mailboxes, you're 80% there.Low
Git fundamentalsclone, pull, commit, push, branch, merge. Six commands cover 95% of daily use.Medium
YAML/JSON literacyReading and editing structured text files. If you can follow an XML config file, you can do this.Low
Pipeline conceptsUnderstanding that "a thing runs automatically when I push code" is the mental model. The syntax is learnable.Medium

None of this is insurmountable. The challenge isn't intellectual—it's cultural. It requires accepting that the way you've been working, while functional, has limitations that become increasingly painful at scale.


The Real Barrier: Process Discipline

Here's the uncomfortable truth: Configuration as Code requires discipline that console-clicking doesn't.

When you can just log into Intune and change a setting, there's no friction. Need to fix something urgently? Click, click, done. The problem is that this lack of friction is precisely what enables the drift and inconsistency you're trying to eliminate.

Configuration as Code introduces deliberate friction:

  1. You make changes in a file, not a console
  2. You commit those changes to a branch
  3. You raise a pull request
  4. Someone reviews it (even if that someone is future-you, forced to articulate why)
  5. Automated checks validate the syntax
  6. The change merges and deploys
1Edit filenot a console2Committo a branch3Pull requestraise a PR4Reviewsecond pair of eyes5Validateautomated checks6Deploymerge & apply
Deliberate friction — slower to change, but every change is governed

This feels slower. Initially, it is slower. But the trade-off is that every change is considered, documented, and reversible. The "urgent fix" that would have taken two minutes in the console now takes fifteen—but it doesn't spawn three follow-up incidents when someone realises the fix broke something else, and nobody remembers what the original setting was.

Organisations that struggle with Configuration as Code usually aren't failing at the technical implementation. They're failing at the cultural shift: accepting that the short-term convenience of console access isn't worth the long-term cost of ungoverned change.


Getting Started: A Pragmatic Path

Don't try to boil the ocean. The goal isn't to export your entire tenant on day one and never touch the console again. That's a recipe for failure.

INCREASING MATURITY1Read-OnlyVisibilityHistorical recordof all config2DriftDetectionRepo becomes thesource of truth3PipelineDeploymentAutomated, testedchange delivery4FullGitOpsBranch protection &auto-remediationWEEK 1MONTH 1MONTH 3MONTH 6+
Each phase delivers incremental value — you don't need to reach Phase 4 to benefit

Phase 1: Read-Only Visibility

Start by using Microsoft365DSC purely for export and documentation. No pipelines, no automation, no enforcement.

# Install the module
Install-Module Microsoft365DSC -Force
 
# Export your Intune configuration
Export-M365DSCConfiguration -Components @(
    'IntuneDeviceConfigurationPolicy',
    'IntuneDeviceCompliancePolicy',
    'IntuneWindowsUpdateForBusinessPolicy'
) -Path 'C:\M365Export'

Commit the output to a Git repository. Set up a scheduled task to re-export weekly. Now you have:

  • A historical record of your configuration over time
  • The ability to diff between exports and see what changed
  • Documentation that stays current without manual effort

This delivers value immediately with minimal risk. Nobody's workflow changes; you're just adding visibility.

Phase 2: Drift Detection

Once you're comfortable with the exports, add drift detection. Compare your repository (desired state) against the live tenant (actual state) on a schedule.

# Compare and generate a drift report
Assert-M365DSCConfiguration -Configuration 'C:\M365Export\M365Config.ps1' -Verbose

Wire this into alerting. When drift is detected, you now have a decision point: was this an authorised change that needs to be committed to the repo, or an unauthorised change that needs to be reverted?

At this stage, you're still not enforcing via pipeline. You're detecting and responding manually. But you've established the principle that the repository is the source of truth.

Phase 3: Pipeline-Driven Deployment

Only after phases 1 and 2 are bedded in should you move to automated deployment. By this point:

  • Your team is comfortable with Git workflows
  • You have confidence that your exported configuration is accurate
  • You've developed the discipline to treat the repo as authoritative

A minimal pipeline (Azure DevOps or GitHub Actions) looks like:

# .github/workflows/deploy-intune.yml
name: Deploy Intune Configuration
 
on:
  push:
    branches: [main]
    paths: ['intune/**']
 
jobs:
  deploy:
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v4
 
      - name: Install Microsoft365DSC
        shell: pwsh
        run: Install-Module Microsoft365DSC -Force
 
      - name: Apply Configuration
        shell: pwsh
        env:
          TENANT_ID: ${{ secrets.TENANT_ID }}
          CLIENT_ID: ${{ secrets.CLIENT_ID }}
          CLIENT_SECRET: ${{ secrets.CLIENT_SECRET }}
        run: |
          # Authentication and deployment logic here
          Start-DSCConfiguration -Path './intune/compiled' -Wait -Force

The specifics will vary based on your authentication model (certificate vs. client secret, managed identity if running in Azure, etc.), but the principle remains: changes merged to main are automatically applied to the tenant.

Phase 4: Full GitOps with Branch Protection

The mature state includes:

  • Branch protection rules: No direct commits to main; all changes via PR
  • Required reviewers: At least one approval before merge
  • Automated validation: Syntax checks, policy compliance checks, and ideally deployment to a test tenant before production
  • Scheduled drift remediation: Detected drift is automatically corrected, not just alerted

This is the target, but it might be 6-12 months away from where you start. That's fine. Each phase delivers incremental value.


Common Objections and Responses

"What about urgent fixes?"

Define an emergency change process. Yes, someone can log into the console for a genuine P1 incident—but the expectation is that the change is immediately backported to the repo. If you find yourself using the emergency process regularly, that's a signal your standard process is too slow or cumbersome.

"Our team doesn't have time to learn this."

You don't have time not to. Every hour spent firefighting configuration drift, investigating "what changed?", or manually documenting settings is an hour you could reclaim with automation. The investment pays off; the question is whether you can afford the short-term velocity dip while the team upskills.

"We tried Git before and it was a mess."

Git is not the problem. Inconsistent practices are.

Git is simple in concept but has sharp edges. Invest in proper training—not a YouTube video, but structured learning with exercises. Establish conventions early: branching strategy, commit message format, PR templates. Most Git disasters come from inconsistent practices, not the tool itself.

"Microsoft365DSC isn't mature enough."

It's been in active development since 2018 and covers the vast majority of Intune and M365 configuration. Are there gaps? Yes. Are there occasional bugs? Yes. Is it better than the alternative of ungoverned console changes? Absolutely. Perfect is the enemy of good.


The Payoff

Six months into a Configuration as Code practice, you'll wonder how you ever operated without it. The benefits compound:

  • New team members can understand your configuration by reading the repo, not by clicking through hundreds of portal screens
  • Audit and compliance requests become "here's the Git history" rather than week-long evidence-gathering exercises
  • Test environments can be spun up with confidence that they match production
  • You can answer "what changed?" in seconds, not hours

More importantly, your team starts thinking differently. Changes become deliberate. Documentation becomes automatic. "I'll just quickly tweak this setting" becomes "I'll raise a PR for this change."

That cultural shift—from reactive console jockeys to proactive configuration engineers—is the real value. The tooling is just the enabler.


Next Steps

If this resonates, here's your homework:

  1. Install Microsoft365DSC and run an export of your Intune configuration. Just look at what comes out.
  2. Create a Git repository (Azure DevOps, GitHub, Gitea—doesn't matter) and commit the export.
  3. Set a calendar reminder to re-export in a week. Compare the two exports.

That's it. No pipelines, no automation, no process changes. Just visibility. See what you learn.


The future of configuration management

One more thought: everything above assumes you're the one writing the code and making the commits. That assumption has an expiry date. More on that soon.


This is Part 2 in a series on endpoint standardisation. Part 1: The Case for Standardisation covers the broader principles. Part 3 will explore application packaging strategies—Win32, MSIX, and when to use each.