In this blog I’ll cover how to list, get, create, update, delete and assign PowerShell scripts in Intune using Microsoft Graph and PowerShell.
Although you can use the Invoke-WebRequest or Invoke-RestMethod cmdlets when working with MS Graph, I prefer to use the Microsoft.Graph.Intune module, aka Intune PowerShell SDK, as it more nicely handles getting an auth token and we don’t have to create any headers, so get that module installed.
In the Graph API, PowerShell scripts live under the deviceManagementScript resource type and these are still only available in the beta schema so they are subject to change.
Connect to MS Graph
First off, let’s connect to MS Graph and set the schema to beta:
If ((Get-MSGraphEnvironment).SchemaVersion -ne "beta")
{
$null = Update-MSGraphEnvironment -SchemaVersion beta
}
$Graph = Connect-MSGraph
List PowerShell Scripts
Now we can list the PowerShell scripts we have in Intune:
$URI = "deviceManagement/deviceManagementScripts"
$IntuneScripts = Invoke-MSGraphRequest -HttpMethod GET -Url $URI
If ($IntuneScripts.value)
{
$IntuneScripts = $IntuneScripts.value
}
If we take a look at the results, we’ll see that the script content is not included when we list scripts. It is included when we get a single script, as we’ll see next.
Get a PowerShell Script
To get a specific script, we need to know its Id. To get that, first let’s create a simple function where we can pass a script name and use the Get method to retrieve the script details.
Function Get-IntunePowerShellScript {
Param($ScriptName)
$URI = "deviceManagement/deviceManagementScripts"
$IntuneScripts = Invoke-MSGraphRequest -HttpMethod GET -Url $URI
If ($IntuneScripts.value)
{
$IntuneScripts = $IntuneScripts.value
}
$IntuneScript = $IntuneScripts | Where {$_.displayName -eq "$ScriptName"}
Return $IntuneScript
}
Now we can use this function to get the script Id and then call Get again adding the script Id to the URL:
$ScriptName = "Escrow Bitlocker Recovery Keys to AAD"
$Script = Get-IntunePowerShellScript -ScriptName $ScriptName
$URI = "deviceManagement/deviceManagementScripts/$($Script.id)"
$IntuneScript = Invoke-MSGraphRequest -HttpMethod GET -Url $URI
If we look at the result, we can see that the script content is now returned, albeit in binary form:
View Script Content
To view the script, we simply need to convert it:
$Base64 =[Convert]::FromBase64String($IntuneScript.scriptContent)
[System.Text.Encoding]::UTF8.GetString($Base64)
Create a Script
Now lets create a new script. To create a script we will read in a script file and convert it into base64. We add this together with other required parameters into some JSON before posting the request.
When reading and converting the script content use UTF8. Other character sets may not decode properly at run-time on the client-side and result in script execution failure.
$ScriptPath = "C:\temp"
$ScriptName = "Escrow-BitlockerRecoveryKeys.ps1"
$Params = @{
ScriptName = $ScriptName
ScriptContent = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((Get-Content -Path "$ScriptPath\$ScriptName" -Raw -Encoding UTF8)))
DisplayName = "Escrow Bitlocker Recovery Keys"
Description = "Backup Bitlocker Recovery key for OS volume to AAD"
RunAsAccount = "system" # or user
EnforceSignatureCheck = "false"
RunAs32Bit = "false"
}
$Json = @"
{
"@odata.type": "#microsoft.graph.deviceManagementScript",
"displayName": "$($params.DisplayName)",
"description": "$($Params.Description)",
"scriptContent": "$($Params.ScriptContent)",
"runAsAccount": "$($Params.RunAsAccount)",
"enforceSignatureCheck": $($Params.EnforceSignatureCheck),
"fileName": "$($Params.ScriptName)",
"runAs32Bit": $($Params.RunAs32Bit)
}
"@
$URI = "deviceManagement/deviceManagementScripts"
$Response = Invoke-MSGraphRequest -HttpMethod POST -Url $URI -Content $Json
We can now see our script in the portal:

Update a Script
To update an existing script, we follow a similar process to creating a new script, we create some JSON that contains the updated parameters then call the Patch method to update it. But first we need to get the Id of the script we want to update, using our previously created function:
$ScriptName = "Escrow Bitlocker Recovery Keys"
$IntuneScript = Get-IntunePowerShellScript -ScriptName $ScriptName
In this example I have updated the content in the source script file so I need to read it in again, as well as updating the description of the script:
$ScriptPath = "C:\temp"
$ScriptName = "Escrow-BitlockerRecoveryKeys.ps1"
$Params = @{
ScriptName = $ScriptName
ScriptContent = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((Get-Content -Path "$ScriptPath\$ScriptName" -Raw -Encoding UTF8)))
DisplayName = "Escrow Bitlocker Recovery Keys"
Description = "Backup Bitlocker Recovery key for OS volume to AAD (Updated 2020-03-19)"
RunAsAccount = "system"
EnforceSignatureCheck = "false"
RunAs32Bit = "false"
}
$Json = @"
{
"@odata.type": "#microsoft.graph.deviceManagementScript",
"displayName": "$($params.DisplayName)",
"description": "$($Params.Description)",
"scriptContent": "$($Params.ScriptContent)",
"runAsAccount": "$($Params.RunAsAccount)",
"enforceSignatureCheck": $($Params.EnforceSignatureCheck),
"fileName": "$($Params.ScriptName)",
"runAs32Bit": $($Params.RunAs32Bit)
}
"@
$URI = "deviceManagement/deviceManagementScripts/$($IntuneScript.id)"
$Response = Invoke-MSGraphRequest -HttpMethod PATCH -Url $URI -Content $Json
We can call Get on the script again and check the lastModifiedDateTime entry to verify that the script was updated, or check in the portal.
Add an Assignment
Before the script will execute anywhere it needs to be assigned to a group. To do that, we need the objectId of the AAD group we want to assign it to. To work with AAD groups I prefer to use the AzureAD module, so install that before continuing.
We need to again get the script that we want to assign:
$ScriptName = "Escrow Bitlocker Recovery Keys"
$IntuneScript = Get-IntunePowerShellScript -ScriptName $ScriptName
Then get the Azure AD group:
$AzureAD = Connect-AzureAD -AccountId $Graph.UPN
$GroupName = "Intune - [Test] Bitlocker Key Escrow"
$Group = Get-AzureADGroup -SearchString $GroupName
Then we prepare the necessary JSON and post the assignment
$Json = @"
{
"deviceManagementScriptGroupAssignments": [
{
"@odata.type": "#microsoft.graph.deviceManagementScriptGroupAssignment",
"id": "$($IntuneScript.Id)",
"targetGroupId": "$($Group.ObjectId)"
}
]
}
"@
$URI = "deviceManagement/deviceManagementScripts/$($IntuneScript.Id)/assign"
Invoke-MSGraphRequest -HttpMethod POST -Url $URI -Content $Json
To replace the current assignment with a new assignment, simply change the group name and run the same code again. To add an additional assignment or multiple assignments, you’ll need to post all the assignments at the same time, for example:
$GroupNameA = "Intune - [Test] Bitlocker Key Escrow"
$GroupNameB = "Intune - [Test] Autopilot SelfDeploying Provisioning"
$GroupA = Get-AzureADGroup -SearchString $GroupNameA
$GroupB = Get-AzureADGroup -SearchString $GroupNameB
$Json = @"
{
"deviceManagementScriptGroupAssignments": [
{
"@odata.type": "#microsoft.graph.deviceManagementScriptGroupAssignment",
"id": "$($IntuneScript.Id)",
"targetGroupId": "$($GroupA.ObjectId)"
},
{
"@odata.type": "#microsoft.graph.deviceManagementScriptGroupAssignment",
"id": "$($IntuneScript.Id)",
"targetGroupId": "$($GroupB.ObjectId)"
}
]
}
"@
$URI = "deviceManagement/deviceManagementScripts/$($IntuneScript.Id)/assign"
Invoke-MSGraphRequest -HttpMethod POST -Url $URI -Content $Json
Delete an Assignment
I haven’t yet figured out how to delete an assignment – the current documentation appears to be incorrect. If you can figure this out please let me know!
Delete a Script
To delete a script, we simply get the script Id and call the Delete method on it:
$ScriptName = "Escrow Bitlocker Recovery Keys"
$IntuneScript = Get-IntunePowerShellScript -ScriptName $ScriptName
$URI = "deviceManagement/deviceManagementScripts/$($IntuneScript.Id)"
Invoke-MSGraphRequest -HttpMethod DELETE -Url $URI