Intune Remediations 101 – Intune’s hidden secret!

As I’m sure some of you know, I’m a big fan of Powershell scripting and find most tasks can be done far more quickly with a script than manually.

Whilst the Powershell scripts within Intune work nicely, they are run-once scripts (unless you want to start deleting registry keys) and sometimes you want a script which runs regularly.

This is where (Proactive) Remediations fits in nicely, think of it as Scheduled Tasks for Intune (but with some added logic), but they can be difficult to get your head around at first so I’ll go through how they work and the steps involved in this post.

Firstly, if you’ve never used them before, you’ll find them in the Devices – Scripts and remediations and then select the remediation tab:

The first time you load it, you’ll have to confirm you are licensed to use it, I’m assuming if you’re reading this that you must be.

Once inside, it’s split into 3 sections: Detection Script, Remediation Script and Assigment (which includes how often it runs)

Detection Script

This is the logic that decides if the second remediation script will run or not and it can be absolutely anything you can query with powershell.

What you are looking at here are the exit codes. If Intune is given an exit code of 0, it will NOT run the remediation script, this is a clean exit and the machine has (or doesn’t have) whatever you are looking for.

On the other hand, if it finds the exit code is 1, this will trigger the next script to run.

For example, if I’m looking for a particular registry key (in this case fastboot), the script will look like this:

$Path = "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Power"
$Name = "HiberbootEnabled"
$Type = "DWORD"
$Value = 0

Try {
    $Registry = Get-ItemProperty -Path $Path -Name $Name -ErrorAction Stop | Select-Object -ExpandProperty $Name
    If ($Registry -eq $Value){
        Write-Output "Compliant"
        Exit 0
    } 
    Write-Warning "Not Compliant"
    Exit 1
} 
Catch {
    Write-Warning "Not Compliant"
    Exit 1
}

At the top we just set what we are expecting to see in a good machine.

Then we check if the registry key exists at all with the Try code block. Obviously if it doesn’t exist, we catch the error and error out with a code 1:

}   
 Write-Warning "Not Compliant"
    Exit 1
}

If the key exists, we then query the value of it to make sure it matches what we are looking for. If we find a match, exit with a code 0, that machine is fine and does not require anything else to run. If the value doesn’t match, we need to sort that!

Registry key is just one example though, we could also use:

Does a file exist?

$File = "C:\windows\system32\notepad.exe"
if (!$file) {
    write-host "Not found"
    exit 1
}
else {
    write-host "Found"
    exit 0
}

Or machines which haven’t rebooted in the last 3 days:

$uptime = get-uptime
if ($uptime.days -gt 3) {
    write-host "Not rebooted in last 3 days"
    exit 1
}
else {
    write-host "All fine, recently rebooted"
    exit 0
}

I’m sure you get the idea!

Remediation Script

This is the part which actually fixes whatever you were initially looking for. We don’t have to worry about exit codes for this one, it runs regardless.

Logging can be found in the Intunemanagementextension.log file, but for ease, I prefer to add my own logging via the start-transcript functionality. I often add the same logic into the remediation script as well just to be extra careful, all it takes is one typo in the detection and it could remediate everything!

So in the case of the fastboot, we simply set the reg key:

Start-Transcript -Path $env:TEMP\DisableFastBoot.txt
if((Test-Path -LiteralPath "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Power") -ne $true) {  
  write-host "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Power does not exist"
  New-Item "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Power" -force -ea SilentlyContinue
write-host "Key Created" };
New-ItemProperty -LiteralPath 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Power' -Name 'HiberbootEnabled' -Value 0 -PropertyType DWord -Force -ea SilentlyContinue;
write-host "Value Set"

Stop-Transcript

For the missing file, we could download it from GitHub/Azure Blob etc.

Start-Transcript -Path $env:TEMP\copyfile.log
$File = "C:\windows\system32\notepad.exe"
if (!$file) {
    write-host "File not found, downloading"
$fileurl = "https://my.file.to.download"

#Set the download location
$output = "c:\my.location\my.file.to.download"

#Download it
Invoke-WebRequest -Uri $appurl -OutFile $output -Method Get
write-host "file downloaded to $output"
}
Stop-Transcript

Or for the machine that hasn’t restarted, we could force a reboot

shutdown /t 300 /r /c "Your machine will restart in 5 minutes, please save any work now"

Scheduling

Once your scripts are created and tested, the next step is to assign them to a group, all users etc.

Once assigned, navigate back to the assignment screen and you can see your options for scheduling (and filtering if required)

Your options here are:

Once (specify date and time)
Hourly (specify how many hours apart between runs)
Daily (specify how many days apart and at what time)

Hopefully that has taken some of the mystery away and everyone will start to use this excellent feature! If there are any particular remediations you want me to add code for, just let me know in the comments.

30 thoughts on “Intune Remediations 101 – Intune’s hidden secret!”

  1. Thank you!!!

    I do have one instance where I want to delete a reg keys below I found the path and ps script
    but not sure how to incorporate the code above

    remove-Item -Path “HKLM:\SYSTEM\CurrentControlSet\Control\EAS”
    remove-Item -Path “HKLM:\SOFTWARE\Microsoft\PolicyManager\current\device\DeviceLock”

    Was thinking this
    Try {
    $Registry = Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\EAS
    $Registry2 = Get-ItemProperty -Path “HKLM:\SOFTWARE\Microsoft\PolicyManager\current\device\DeviceLock”

    \\This is where I want to delete but what should I give as parameter

    If ($Registry -eq $True?){
    Write-Output “EAS Present”
    remove-Item -Path HKLM:\SYSTEM\CurrentControlSet\Control\EAS
    Exit 0
    }
    ElseIf ($Registry2 -eq $Value){
    Write-Output “Compliant”
    remove-Item -Path HKLM:\SOFTWARE\Microsoft\PolicyManager\current\device\DeviceLock
    Exit 0
    }
    Else {
    Write-Warning “No EAS”
    Exit 1
    }
    }
    Catch {
    Write-Warning “No Device Lock”
    Exit 1
    }

    Reply
    • Hi,
      This is where you need to split them, you have one script which detects and one which remediates.
      Remove the remove-item parts and that gives a detection script (although you aren’t declaring a $value for reg2).

      Then the remove items sit on their own in a remediation script which only runs if the detection is non-compliant

      Reply
  2. Very cool. How about checking the registry for a key and if it is now found, set it?
    Example: Cloud Kerberos Ticket at logon.
    Can be found at 2 different location – depending on distribution method:
    Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\Parameters
    or
    Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Parameters

    The value is always:
    CloudKerberosTicketRetrievalEnabled
    REG_DWORD 1

    Reply
    • Something like this for the detection:
      $Path = “HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\Parameters”
      $path2 = “HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\Kerberos\Parameters”
      $Name = “CloudKerberosTicketRetrievalEnabled”
      $Value = 1

      Try {
      $Registry = Get-ItemProperty -Path $Path -Name $Name -ErrorAction Stop | Select-Object -ExpandProperty $Name
      $Registry2 = Get-ItemProperty -Path $Path2 -Name $Name -ErrorAction Stop | Select-Object -ExpandProperty $Name

      If ($Registry -eq $Value){
      Write-Output “Compliant”
      Exit 0
      }
      ElseIf ($Registry2 -eq $Value){
      Write-Output “Compliant”
      Exit 0
      }
      Else {
      Write-Warning “Not Compliant”
      Exit 1
      }
      }
      Catch {
      Write-Warning “Not Compliant”
      Exit 1
      }

      Reply
  3. Has this changed location I can’t find it. Closet I have found is Devices>Scripts and remediations>Remediations.

    Has this moved and do you need to update the page? also I presume it will be the same if so

    Thanks

    Reply
  4. If I want to run a script once and want it to run before user logins for the first time (during enrollment), will powershell script be a best option or proactive remediation?

    Reply
  5. Hi Andrew
    I’m looking for ‘where to start’ really.
    All our users are remote & running Windows 10 Pro
    The support guy before me used to have Barracuda remote for accessing devices but that contract expired and now I have a fresh Barracuda RMM access, however, I’m struggling to get it installed on most devices because of a ‘previous installation’ that I can’t easily get around, because somethings won’t uninstall.
    For some of the devices, I’ve done the… uninstall – reboot – registry hack – uninstall again & about 5 reboots and managed to get through, but it’s not often that works.
    Others, I’ve got the users to ship the PC back to me, I’ve reformatted it and then sent it out (because it installs on a ‘new’ PC)
    I’ve still got another 50 or so PC’s to get done.
    Is there a script of some sort I can run through intune to force the uninstall please?
    The program is Barracuda RMM.
    Barracuda support… let’s say they are less than helpful!
    Thank you in advance.

    Reply
  6. Hi Andrew,

    I have a slightly different question, about remediation scripts in general. Once a script is deployed on client machines, how can it be stopped? I removed the group I had assigned it to, but it still runs!

    Reply
  7. Hi Andrew Taylor,

    Thanks for writing this script. I would like to know about this script as I am not much experienced on script writing and I wanted to deploy the script to reboot the devices once in a week at least.

    Can you tell me the correct path/script which I need to use from this blog and I can deploy it via Remediation and it will do the needful.

    Reply
  8. Thanks for the quick response, Andrew.
    Your solution is better, because all can be implemented in only 1 proactive remediation script.
    And also do you think that MS Graph can be used for such kind of detailed reports?

    Reply
  9. Hi Andrew,
    I am wondering if a remediation script is used for uninstallation of unwanted software, how we can have a detailed report how many times the script has been removed this SW from one and the same device for a period of time?
    I think that I can create a reg key which flags the existence of the SW before the removal and with second remediation script to catch this reg key and to take an info about the device in a csv file on the blob..
    But is there more elegant solution for detailed report?

    Reply
    • Hi,
      The device status will show you the results and should repeat for each machine.
      You can also access the output of a proactive remediation script so another option would be:
      1) Set a reg key with a value of 1
      2) Each time it runs, read the reg key, increment and replace the key value
      3) Output the reg key value at the end.

      That way it will show you how many times it has run against each machine

      Reply
  10. Hi Andrew
    Proactive remediation has become one of my favourite tools in MEM. I do have one pretty niche requirement for a customer where they want to be able to apply settings based on the “Primary User” attribute in MEM.

    Is there any way “Primary User” can be injected into the detection and remediation script?

    Reply
  11. great article. something I’d like to point out it’s the Invoke-WebRequest usage
    I replace by azcopy https://docs.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-v10
    I’ve noticed Invoke-WebRequest breaking a lot regardless of some tweaks around http/https and TLS settings when downloading from azure blob storage
    I place c:\programdata\%workingdir%\azcopy and from there I call the files I need to download and validate files hash every time to make sure the file is ok then I proceed with all other requirements.

    Reply
    • Thanks Thiago! I’m a big fan of azcopy and use it regularly on my machines, just a shame I can’t script the installation of it without ending up using an invoke-webrequest to grab it from somewhere.

      Ideally a powershell script or module for AZCopy would make life a lot easier.

      Reply
    • Is that just a matter of specifying TLS 1.2? I’ve incorporated the following into any script that has to make web or API calls, just because of the unpredictability, and I haven’t had TLS related errors since.
      [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

      Reply

Leave a Comment