Creating Intune Proactive Remediation via Powershell

Proactive Remediations are one of my favourite features within Intune, but I’m a fan of automation so I thought I’d do some digging in MS Graph to see how much I can automate.

Before I start, if you haven’t installed it yet, go and get the Graph X-Ray extension for Edge/Chrome from here. It will show you exactly what’s happening behind the scenes when working in Intune.

Now, onto the script. For this example I’m doing a simple reg key, but you’ll be able to see the powershell code itself is inline so it can accept anything.

As usual, you can download a copy from github here

First, let’s set some variables

$DisplayName = "Remediate Fastboot Automated"
$Description = "This was created via PowerShell!"
$Publisher = "Andrew Taylor"
##RunAs can be "system" or "user"
$RunAs = "system"
##True for 32-bit, false for 64-bit
$RunAs32 = $true
##Daily or Hourly
$ScheduleType = "Daily"
##How Often
$ScheduleFrequency = "1"
##Start Time (if daily)
$StartTime = "01:00"
$AADGroupName = "Intune-Users"

The Start Time is only required if the schedule is set to Daily

Now we need to set the detection script

$detect = @'
$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
}
'@

It can be anything you would normally write in PowerShell

Then we need the remediation script

$remediate = @"
New-ItemProperty -LiteralPath 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Power' -Name 'HiberbootEnabled' -Value 0 -PropertyType DWord -Force -ea SilentlyContinue;
"@

The script needs the Microsoft.Graph.Intune and AzureAD modules, I won’t list the code here, but feel free to grab it from the script, it’s a simple install if not detected and then import.

Once we have connected to both, we can start the building

The all important JSON to build the thing

$params = @{
         displayName = $DisplayName
         description = $Description
         publisher = $Publisher
         runAs32Bit = $RunAs32
         runAsAccount = $RunAs
         enforceSignatureCheck = $false
         detectionScriptContent = [System.Text.Encoding]::ASCII.GetBytes($detect)
         remediationScriptContent = [System.Text.Encoding]::ASCII.GetBytes($remediate)
         roleScopeTagIds = @(
                 "0"
         )
}

As you can see, we are converting the PS Code to ASCII to get it uploaded. If you use Scope tags, you can edit them here

To deploy it:

$graphApiVersion = "beta"
$Resource = "deviceManagement/deviceHealthScripts"
$uri = "https://graph.microsoft.com/$graphApiVersion/$Resource"

try {
        $proactive = Invoke-MGGraphRequest -Uri $uri -Method Post -Body $params -ContentType "application/json" 

}
catch {
    Write-Error $_.Exception 
    
}

Proactive Remediations all use the “DeviceHealthScripts” within Graph

Now, we need to assign it, it’s similar to any other policy, but this one has a schedule attached.

Quickly grab the group ObjectID:

$AADGroupID = (get-mggroup| where-object DisplayName -eq $AADGroupName).ObjectID

For Daily:

    $params = @{
        DeviceHealthScriptAssignments = @(
            @{
                Target = @{
                    "@odata.type" = "#microsoft.graph.groupAssignmentTarget"
                    GroupId = $AADGroupID
                }
                RunRemediationScript = $true
                RunSchedule = @{
                    "@odata.type" = "#microsoft.graph.deviceHealthScriptDailySchedule"
                    Interval = $scheduleFrequency
                    Time = $StartTime
                    UseUtc = $false
                }
            }
        )
    }

Or hourly:

$params = @{
	DeviceHealthScriptAssignments = @(
		@{
			Target = @{
				"@odata.type" = "#microsoft.graph.groupAssignmentTarget"
				GroupId = $AADGroupID
			}
			RunRemediationScript = $true
			RunSchedule = @{
				"@odata.type" = "#microsoft.graph.deviceHealthScriptHourlySchedule"
				Interval = $scheduleFrequency
			}
		}
	)
}

Finally grab the ID of the remediation and assign it to the AAD Group

$remediationID = $proactive.ID


$graphApiVersion = "beta"
$Resource = "deviceManagement/deviceHealthScripts"
$uri = "https://graph.microsoft.com/$graphApiVersion/$Resource/$remediationID/assign"

try {
        $proactive = Invoke-MGGraphRequest -Uri $uri -Method Post -Body $params -ContentType "application/json" 
}
catch {
    Write-Error $_.Exception 
    
}

Using this you could easily deploy multiple remediations across tenants without having to log into the GUI on each

12 thoughts on “Creating Intune Proactive Remediation via Powershell”

  1. this is one best site i ever visit, when i ran script i got folowing error message

    Assigning Hourly Schedule running every 1 hours
    C:\temp\create-proactive-remediation.ps1 : Microsoft.Graph.PowerShell.Authentication.Helpers.HttpResponseException: Response status code does not indicate success: InternalServerError (Internal Server Error).
    at System.Management.Automation.MshCommandRuntime.ThrowTerminatingError(ErrorRecord errorRecord)
    At line:1 char:1
    + .\create-proactive-remediation.ps1
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,create-proactive-remediation.ps1

    Remediation Assigned
    Complete

    Reply
  2. Hi!
    After “Creating Proactive Remediation” I still gets:
    Microsoft.Graph.PowerShell.Authentication.Helpers.HttpResponseException: Response status code does not indicate
    success: InternalServerError (Internal Server Error).
    at System.Management.Automation.MshCommandRuntime.ThrowTerminatingError(ErrorRecord errorRecord)

    and of course notihng is created :/ I’m wondering if I’m doing something wrong or Microsoft changed something again :/

    Reply
  3. Great blog and something that will help me on this project I am on right now. I am trying to run this script, a little edited but it fails on the Invoke-MGGraphRequest step. Almost like Microsoft changed something.

    $proactive = Invoke-MGGraphRequest -Url $uri -Method Post -Content $params

    This was part of the error:

    System.Management.Automation.ValidationMetadataException: The cmdlet cannot run because the -ContentType parameter is not a valid Content-Type header. Specify a valid
    Content-Type for -ContentType, then retry. To suppress header validation, supply the -SkipHeaderValidation parameter. —> System.FormatException: The format of value
    ‘System.Collections.Hashtable’ is invalid.

    Reply
    • Hi Kris,

      Sorry, that’s on me, I updated the script to use the new Graph SDK instead of the deprecated AAD one and forgot to switch the commands in the query. It should be sorted now

      Reply

Leave a Comment