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
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
Glad you like the site! Can you share your script and I’ll have a look for you
This is same script that you provided, I ran this script twice
Which assignment are you using?
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 :/
Can you share your script?
I used your script as an example on my test tenant. Script from GitHub https://github.com/andrew-s-taylor/public/blob/main/Powershell%20Scripts/Intune/create-proactive-remediation.ps1
I tried also modify it, but always end with the same error :/
Try now, the params had capitalized themselves so didn’t match what it was expecting
You are my hero <3
Now it works. Thanks a lot 🙂
Thanks! This blog pointed me the right direction
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.
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