Our Thinking / Field Notes / The Guide

STEP-BY-STEP GUIDANCE / MICROSOFT INTUNE

Intune PowerShell Scripts vs Remediations: Deploy and Verify Safely

Intune platform scripts run once and report back; Remediations check on a schedule and fix a problem only when they find it. Learn which to use, how context and scope work, and how to test, deploy, verify and roll back a small example.

Start here

A platform script is a one-off push: it runs once per assignment, retries a few times if it fails, and runs again only if you change it. A Remediation is a detection script plus an optional fix, run on a schedule, and the fix only runs when the detection script exits with code 1. Remediations need a qualifying Windows licence on top of Intune. The safe route for both is the same: write a read-only check first, test it locally in the same context Intune will use, assign it to one test device group only, confirm the result in the admin centre and in the device logs, and only then widen the audience.

Platform scripts or Remediations: which one do you need?

Both deliver PowerShell to Windows devices through the same background agent. The difference is what happens afterwards. A platform script says "do this once". A Remediation says "keep checking this, and put it right if it has drifted".

If you find yourself using a platform script to make sure something stays set, you have really built a Remediation without the paperwork. And if the thing you want to change already exists as a setting in the settings catalog, use the policy rather than a script: policy is easier to report on and easier to undo. Scripts are for the gaps between settings.

One naming wrinkle: Microsoft renamed Proactive Remediations to Remediations. Older blogs and forum threads still use the old name, so search for both.

  • Platform scripts run once per assignment. If a script fails, the agent retries it at the next three consecutive check-ins, then stops until you change the script or the assignment. A platform script times out after 30 minutes. They need only your normal Intune licensing.
  • Remediations are script packages: a detection script, plus an optional remediation script. They run on a schedule you choose (once, hourly or daily), the remediation script runs only when detection exits with code 1, and the portal reports how many devices had the issue. They need an additional Windows licence, covered next.

What you need before you start

Keep this chapter's practice to a single test device. A Windows 11 virtual machine is ideal because you are going to break something on purpose, and you will want to be able to put it back.

  • Licensing for Remediations: users of the devices need Windows Enterprise E3 or E5 (included in Microsoft 365 F3, E3 or E5), Windows Education A3 or A5, or Windows Virtual Desktop Access per user. Microsoft also expects an Intune Service Administrator to confirm licensing before Remediations are used for the first time. Microsoft 365 Business plans are not named in that list, so do not assume they qualify - check your own entitlement before building anything on this feature.
  • Devices: Windows (not Home, not S mode), Microsoft Entra joined or hybrid joined, and enrolled in Intune. The Intune Management Extension on the device must be version 1.58.103.0 or later; it updates itself, so a device that syncs normally is almost always fine.
  • Your permissions: an Intune role with the relevant Device configurations permissions to create scripts and script packages. To run a Remediation on demand you also need the Run remediation permission (under Remote tasks), and during the preview Organization: Read.
  • A test group: create a Microsoft Entra device security group with a neutral name such as Lab - Scripts - Test devices and put only your test PC in it. Every assignment in this article goes to that group and nothing else.
  • On the test PC: you need local administrator rights, and the free Sysinternals PsExec tool to test scripts as the SYSTEM account.

How the scripts reach the device, and what they run as

The Intune Management Extension (IME) is a small agent service that Intune installs automatically the first time you assign it something that needs it, such as a script or a Remediation. It checks in with Intune every eight hours, independently of the normal MDM check-in. Choosing Sync in the Company Portal app (Settings, then Sync) triggers both, which is how you avoid waiting around during testing.

Three settings decide how much damage a script can do, so decide them deliberately rather than accepting the defaults:

The assignment is the third lever, and the one that most often goes wrong. Assign to Microsoft Entra device groups where you can. A script assigned to a device runs for every new user who signs in to it, whereas a user-targeted script follows the person. When you assign a package, do not mix user groups and device groups across include and exclude. For this chapter, only ever the lab test group.

  • Context. With Run this script using the logged on credentials set to Yes, the script runs as the signed-in user, with their rights. Set to No, it runs in the system context - the all-powerful local SYSTEM account. Anything that edits a service, machine-wide registry keys or protected folders needs system context. Anything that touches the user's own certificate store or HKCU needs user context. Pick the least powerful one that works.
  • Signature check. Enforce script signature check set to Yes means the device's PowerShell execution policy applies: on Windows client that is Restricted by default, so an unsigned script will not run, and a signed one needs its certificate in the device's Trusted Publishers store. Set to No, Intune runs the script with the Bypass policy. For a first lab, No is the sensible choice; signing is a separate project.
  • 64-bit host. Run script in 64-bit PowerShell host controls whether the script runs in a 64-bit or 32-bit PowerShell process. A 32-bit process on a 64-bit PC is silently redirected for some registry and file locations, which can make a script that works at your console behave differently under Intune. Match the host to the one you test in.

Deploy a regular script and prove which context it ran in

Before you touch Remediations, send one harmless platform script and read back the evidence. This script writes a single line to a text file recording which account ran it and whether the process was 64-bit. It changes nothing except creating one small folder under ProgramData.

Save the script as Context-Check.ps1 on your admin PC. Keep it plain ASCII so the encoding is never in question.

powershell
# Context-Check.ps1
# Records which account ran this script and whether the process is 64-bit.
# Only change: creates C:\ProgramData\IntuneLab and one text file inside it.
$dir = Join-Path $env:ProgramData 'IntuneLab'
New-Item -Path $dir -ItemType Directory -Force | Out-Null
$who = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$line = 'Platform script ran as {0}; 64-bit process: {1}' -f $who, [Environment]::Is64BitProcess
Set-Content -Path (Join-Path $dir 'context-check.txt') -Value $line -Encoding ASCII
exit 0
  1. Save the script above as Context-Check.ps1.
  2. In the Microsoft Intune admin center go to Devices, then Scripts and remediations (listed under Manage devices), select the Platform scripts tab, then Add, then Windows 10 and later. On Basics, name it Lab - Context check and add a short description.
  3. On Script settings, upload the file and set: Run this script using the logged on credentials to No, Enforce script signature check to No, and Run script in 64-bit PowerShell host to Yes.
  4. On Assignments, choose Select groups to include and add only your lab test group. Review and select Add. The policy deploys as soon as you save it.
  5. On the test PC, open Company Portal and select Settings, then Sync. The agent may take a few minutes to pick the script up and run it. Carry on when the text file appears.
  6. Read the file. Seeing NT AUTHORITY\SYSTEM and True confirms that the script ran in the system context in a 64-bit process, exactly as configured.
    powershell
    Get-Content -Path "$env:ProgramData\IntuneLab\context-check.txt"
  7. Back in the admin centre, open the script, select Monitor, then Device status. Your test PC should show a success result.

Write a detection and remediation pair that is safe to repeat

The example problem is deliberately small: the Windows Time service has been set to Disabled. That matters more than it sounds, because a device with a badly wrong clock can fail to sign in and, as Microsoft notes, will fail to run Intune scripts at all if the clock is months or years out. It is also easy to cause on a test PC and easy to put back.

The contract between Intune and your detection script is the exit code. Exit 1 means the issue was found and the remediation script should run. Any other exit code, including an empty result, means the remediation does not run. That is why the detection script below exits 2 if it cannot work out the answer: an unexpected error should never be mistaken for permission to change something.

Detection must be read-only. Remediation should do the minimum, do nothing when there is nothing to do, and be safe to run twice. Keep output to a short, stable line (the limit is 2,048 characters, and values that change every run, such as timestamps, make reports noisy). Never put passwords or personal data in scripts or output, and never add a reboot command: Microsoft says not to, and a script that restarts devices on a schedule is not a support tool, it is an incident.

The detection script, Detect-W32TimeDisabled.ps1:

The remediation script, Remediate-W32TimeDisabled.ps1. It makes one change - the startup type goes from Disabled to Manual and the service is started - and reports what it did so the portal output tells the story. Manual is a conservative choice; whether your organisation prefers another start type is your call.

Save both as plain ASCII or UTF-8 text files. Microsoft requires UTF-8 for Remediation scripts, and if you later turn on the signature check it must be UTF-8 without a byte order mark. Keeping the scripts pure ASCII removes the worry; the next section checks it anyway.

powershell
# Detect-W32TimeDisabled.ps1
# Read-only. Reports whether the Windows Time service startup type is Disabled.
# Exit 0 = nothing to fix. Exit 1 = issue found (Intune runs the remediation script).
# Exit 2 = could not determine the state (Intune does not run the remediation script).
try {
    $svc = Get-Service -Name 'W32Time' -ErrorAction Stop
    if ($svc.StartType -eq 'Disabled') {
        Write-Output 'Issue found: W32Time startup type is Disabled'
        exit 1
    }
    Write-Output ('No issue: W32Time startup type is {0}' -f $svc.StartType)
    exit 0
}
catch {
    Write-Output ('Detection error: {0}' -f $_.Exception.Message)
    exit 2
}
powershell
# Remediate-W32TimeDisabled.ps1
# Changes one setting: W32Time startup type Disabled -> Manual, then starts the service.
# Safe to run repeatedly: does nothing if the service is not disabled.
try {
    $svc = Get-Service -Name 'W32Time' -ErrorAction Stop
    if ($svc.StartType -ne 'Disabled') {
        Write-Output ('No change needed: W32Time startup type is {0}' -f $svc.StartType)
        exit 0
    }
    Set-Service -Name 'W32Time' -StartupType Manual -ErrorAction Stop
    Start-Service -Name 'W32Time' -ErrorAction Stop
    Write-Output 'Changed: W32Time startup type Disabled to Manual and service started'
    exit 0
}
catch {
    Write-Output ('Remediation error: {0}' -f $_.Exception.Message)
    exit 1
}

Test the pair on the test PC before Intune sees it

Everything here happens on the lab PC, in an elevated PowerShell window unless stated. The aim is to watch the detection script fail, the remediation fix it and the detection script pass, all in the same context Intune will use, so that the first run through Intune holds no surprises.

  1. Create a folder named C:\LabScripts and save both scripts into it. Recent versions of Notepad save UTF-8 without a byte order mark (BOM) by default, but check rather than assume. A healthy file starts with the bytes 23 20 44 (the characters # and a space, then D or R). EF BB BF means a BOM is present.
    powershell
    foreach ($f in 'Detect-W32TimeDisabled.ps1','Remediate-W32TimeDisabled.ps1') {
        $bytes = [System.IO.File]::ReadAllBytes("C:\LabScripts\$f")
        $hex = ($bytes[0..2] | ForEach-Object { $_.ToString('X2') }) -join ' '
        '{0}: first bytes {1}' -f $f, $hex
    }
    powershell
    $path = 'C:\LabScripts\Detect-W32TimeDisabled.ps1'
    $text = [System.IO.File]::ReadAllText($path)
    [System.IO.File]::WriteAllText($path, $text, (New-Object System.Text.UTF8Encoding($false)))
  2. Create the fault on purpose. This disables the Windows Time service and stops it on this test PC only. Do not do this on a machine you care about.
    powershell
    Set-Service -Name W32Time -StartupType Disabled
    Stop-Service -Name W32Time
  3. Open a SYSTEM-level PowerShell window, since that is the context the Intune settings in this article use. From an elevated command prompt, run PsExec as shown. In the new window, run the detection script through powershell.exe with the Bypass execution policy (which mirrors what Intune does when the signature check is off) and read its exit code. You should see the Issue found message and exit code 1.
    cmd
    psexec -i -s powershell.exe
    powershell
    powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\LabScripts\Detect-W32TimeDisabled.ps1
    $LASTEXITCODE
  4. Run the remediation script the same way, then check the service. You should see exit code 0, the Changed message, and the service showing Running with a Manual start type.
    powershell
    powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\LabScripts\Remediate-W32TimeDisabled.ps1
    $LASTEXITCODE
    Get-Service -Name W32Time | Select-Object Name, Status, StartType
  5. Run the detection script again. This time it should print No issue and return exit code 0. That sequence - 1, fix, 0 - is the whole behaviour you are asking Intune to automate.
    powershell
    powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\LabScripts\Detect-W32TimeDisabled.ps1
    $LASTEXITCODE
  6. Re-create the fault by repeating the two commands from step 2, so that there is something for Intune to find.

Create the Remediation and assign it to the test group

On a lab device carrying a deliberate fault it makes sense to deploy detection and remediation together. In a real environment, start differently: a script package can contain a detection script only, so you can first learn how many devices would be touched, and only then attach a remediation. That is the cheapest safety net the feature offers.

Upload the .ps1 files rather than pasting code into the browser; that keeps the encoding exactly as you saved it.

  1. In the Microsoft Intune admin center go to Devices, then Scripts and remediations (under Manage devices), open the Remediations tab and select Create script package.
  2. On Basics, give it a name such as Lab - W32Time not disabled and a description saying what it checks and what it changes.
  3. On Settings, use the folder icon to upload Detect-W32TimeDisabled.ps1 as the detection script and Remediate-W32TimeDisabled.ps1 as the remediation script. Set Run this script using the logged-on credentials to No (changing a service needs system rights), Enforce script signature check to No, and Run script in 64-bit PowerShell to Yes. Microsoft's own example configuration shows No for 64-bit; these scripts behave the same either way, and Yes matches how you tested them.
  4. Skip scope tags unless you already use them, and select Next.
  5. On Assignments, add only the lab test device group. Edit the schedule: Daily at a time a few minutes ahead of when you plan to sync is enough for a test (Once, Hourly and Daily are the available options, and missed runs happen as soon as the device is online). Leave the clock on the device's local time unless you have a reason to use UTC. Avoid hourly runs for anything that does not need them, since every script costs the device CPU and battery.
  6. Select Review + create, then Create.

Verify it worked, and read the evidence properly

The device collects Remediation policy after a restart of the device or the IME service, after a user signs in, and every eight hours. You do not want to wait eight hours, so force a sync. Reporting also has a rhythm that catches people out: the first run of a recurring script counts as a change and is reported straight away, after that the device reports only changes for up to six days, and every seven days it reports even if nothing changed. A quiet portal after the first day is normal, not a fault.

Checking at three levels - effect, portal and log - tells you whether the script ran, whether Intune heard about it and what the agent thought.

  1. On the test PC, open Company Portal and select Settings, then Sync. Alternatively, sync the device from its page in the admin centre.
  2. Optional shortcut: in the admin centre go to Devices, then By platform, then Windows, select the test PC, then the ellipsis (...) and Run remediation (preview), choose your script package and select Run remediation. This needs the device to be online and reachable through Windows Push Notification Service, and the permission described earlier. Only one on-demand run can be issued to a device at a time.
  3. Wait until the scheduled time has passed and the device has had a few minutes to run it. Then check the effect directly: the service should be Running again with a Manual start type.
    powershell
    Get-Service -Name W32Time | Select-Object Name, Status, StartType
  4. In the admin centre open the script package. The overview shows detection and remediation status counts, and Device status lists each device. Use Export if you want the per-device script output as a CSV; your one-line messages should appear there.
  5. For the device's own view, go to Devices, then By platform, then Windows, select the test PC and open Remediations under Monitor.
  6. For the agent's view, read the HealthScripts log, which tracks Remediations. It sits in the IME logs folder and is best opened in CMTrace, but the tail of the file is enough for a quick look. Script contents can appear in these logs, which is one more reason never to put secrets in a script.
    powershell
    Get-Content -Path "$env:ProgramData\Microsoft\IntuneManagementExtension\Logs\HealthScripts.log" -Tail 60

Roll back and troubleshoot

Rolling back is two separate jobs, and people often only do the first. Stopping the script is not the same as undoing what it already did. The remediation script is the only thing that changed the device, so the undo is a deliberate action, not something that happens when you delete the package.

If you get stuck, these are the usual causes in rough order of likelihood. This chapter does not cover signing scripts with a certificate, custom compliance scripts or Microsoft Graph automation; those are separate topics.

  • Stop future runs: on the package's Properties page, edit Assignments and remove the test group, or delete the package. Devices that were already remediated keep the new setting. Platform scripts work the same way: deleting the script stops further runs but does not remove the folder it created.
  • Undo the lab change on the test PC. This restores the deliberately broken state, so only run it if you want the fault back (or set the start type you actually want in place of Disabled).
    powershell
    Set-Service -Name W32Time -StartupType Disabled
    Stop-Service -Name W32Time
  • Remove the platform script's footprint on the test PC. This deletes only the folder created by the context-check script.
    powershell
    Remove-Item -Path "$env:ProgramData\IntuneLab" -Recurse
  • Nothing happens at all: confirm the device is Microsoft Entra joined (registered-only devices may not receive scripts), is not Windows Home or in S mode, is a member of the test group and has synced. Then check that the agent service is running.
    powershell
    Get-Service -Name IntuneManagementExtension | Select-Object Name, Status, StartType
  • The remediation never runs: it only runs if detection exits with code 1. Empty output or any other exit code counts as no issue found. Also confirm that detection reached the same result in the same context when you tested locally.
  • Different result under Intune than at your console: look at the context and host settings first. A script run as your admin account in a 64-bit window is not the same test as a SYSTEM-context script in a 32-bit host.
  • Script fails with the signature check on: with it set to Yes the device execution policy applies, the signing certificate must be in Trusted Publishers and the file must be UTF-8 without a BOM. A BOM is the classic reason a working script suddenly will not run once signing is switched on.
  • A platform script stopped retrying: after a failure the agent retries at the next three check-ins, then waits. Fix the script and upload the corrected version, which counts as a change and runs again.
  • Intune says it succeeded but nothing changed: Microsoft notes that antivirus software can sandbox the agent's script executor, so check AgentExecutor.log and your security software.
Technical terms explained
BOM
Byte order mark: a few invisible bytes at the start of some text files saved as UTF-8. Remediation scripts with signature checking enabled must be UTF-8 without one.
execution policy
A PowerShell setting that controls which scripts are allowed to run. On Windows client the default is Restricted, which blocks scripts unless they are signed and trusted.
exit code
A number a script returns when it finishes. In Remediations, exit code 1 from the detection script means the issue was found and the remediation should run.
Intune Management Extension
A background service that Intune installs on Windows devices to run PowerShell scripts, Remediations and Win32 app installs. It checks in with Intune about every eight hours.
system context
Running a script as the local SYSTEM account, which has broad rights over the machine, rather than as the signed-in user. Use it only when the task genuinely needs it.
Trusted Publishers
A Windows certificate store listing publishers whose signed scripts the device will trust. Signed scripts need their certificate here when the signature check is enforced.

Sources and further reading

KEEP THE CONVERSATION GOING

Want a second opinion before a script reaches your users?

If you would like help planning a pilot group, reviewing a script before rollout or checking which Windows licences your Remediations need, we are happy to talk it through.

Contact Rismor